Thursday, January 21, 2010

Read and Write logs in XML file

If you dont want to use a DB, but still want to store, read data in a structured way, then xml is the best option. Its also used to send data over the internet.

Lets take a look at how we can create, format, read, and write data into xml file. You need to add using System.xml class with your file

1. Create an xml file:

XmlTextWriter xmltr = new XmlTextWriter(filePath, System.Text.Encoding.UTF8);
xmltr.Flush();
xmltr.Formatting = Formatting.Indented;
xmltr.WriteProcessingInstruction("xml", "version='1.0' encoding='UTF-8'");
xmltr.WriteStartElement("ComponentLog");
xmltr.Close();

We are using XmlTextWriter class to create the file. ComponentLog is the parent node in the xml file. An xml file can contain only 1 parent node.

2. Set format of the xml file:

XmlDocument doc = new XmlDocument();
doc.Load(filePath);
XmlNode node = doc.GetElementsByTagName("*")[0];

XmlAttribute xmlatr = doc.CreateAttribute("xmlns", "xsi", "http://www.w3.org/2000/xmlns/");
xmlatr.InnerText = "http://www.w3.org/2001/XMLSchema-instance";
XmlAttribute xmlatr2 = doc.CreateAttribute("xmlns", "xsd", "http://www.w3.org/2000/xmlns/");
xmlatr2.InnerText = "http://www.w3.org/2001/XMLSchema";

node.Attributes.Append(xmlatr);
node.Attributes.Append(xmlatr2);

XmlNode SQLnode = doc.CreateElement(ComponentType.SQLServer.ToString(), null);
XmlNode IndesignNode = doc.CreateElement(ComponentType.IndesignServer.ToString(), null);
XmlNode FSNode = doc.CreateElement(ComponentType.FS.ToString(), null);
node.AppendChild(SQLnode);
node.AppendChild(IndesignNode);
node.AppendChild(FSNode);
doc.AppendChild(node);
doc.Save(filePath);

filepath is a string contains the full path of the xml file including filename and extension. We use XmlDocument to read the file first, then format it. It would obviously get only 1 node which we created earlier named "ComponentLog". Then we have added 2 attributes with this parent node. Next with this parent ode "ComponentLog" we have added 3 child nodes. We are storing 3 types of log information in the xml file. They are: SQL, Indesign, and FileSystem. That completed our formatting with the xml file.

3. Write log data into xml file:

public static void WriteXmlLog(ComponentType CType, int CStatus, string userId, string userName, string email, DateTime lastMailSent,string culturecode)
{
try
{
if (!File.Exists(filePath))
{
CreateXmlFile();
}

XmlDocument doc = new XmlDocument();

doc.Load(filePath);
XmlNodeList baseNodes = doc.GetElementsByTagName(CType.ToString());

XmlNode node = doc.CreateNode(XmlNodeType.Element, CType.ToString() + "Log", null);

XmlNode UserId = doc.CreateElement("UserId");
UserId.InnerText = userId;

XmlNode UserName = doc.CreateElement("UserName");
UserName.InnerText = userName;

XmlNode Email = doc.CreateElement("Email");
Email.InnerText = email;

XmlNode LastMailSent = doc.CreateElement("LastMailSent");
LastMailSent.InnerText = lastMailSent.ToString();

XmlNode ComponentStatus = doc.CreateElement("ComponentStatus");
ComponentStatus.InnerText = CStatus.ToString();

XmlNode Culturecode = doc.CreateElement("culturecode");
Culturecode.InnerText = culturecode;

// add children to father
node.AppendChild(UserId);
node.AppendChild(UserName);
node.AppendChild(Email);
node.AppendChild(LastMailSent);
node.AppendChild(ComponentStatus);
node.AppendChild(Culturecode);

// append the new node
baseNodes[0].AppendChild(node);

// save the file
doc.Save(filePath);
}
catch (Exception ex)
{

}
}

First, we check whether a file already exists in that path or not. If not, then we create a new one. Then, we get the node that defines type of our log. CType is the enum that defines the type of log data we will write (FS, Indesign, or SQL).
XmlNodeList baseNodes = doc.GetElementsByTagName(CType.ToString()); returns nodes that have same tagname. baseNodes[0] (Lets call this as the "typeRoot" node) will return the topmost node of that type, which is added with the parent node "ComponentLog". with the typeRoot node, we want to add log data. So basically, we have created a node named "node", created respective nodes to store info such as userId, userName, email, lastMailSent, culturecode. Then we have added this info nodes under the newly created node "node", then added "node" under the "typeRoot" node. Under the 'typeRoot" node there will be multiple entries of "node". Their tag name is same to their "typeRoot" node, just the extra "log" string is added to the end of their name. Then we save (overwrite) the existing doc file. The xml file looks like this (using IE):


4. Read from xml file:

public static DataTable ReadXML(ComponentType ctype)
{
DataSet ds = new DataSet();
DataTable dt = new DataTable();
try
{
ds.ReadXml(filePath);
if (ds != null)
{
if (ctype.ToString() == ComponentType.SQLServer.ToString())
{
dt = ds.Tables[ComponentType.SQLServer.ToString() + "Log"];
}
else if (ctype.ToString() == ComponentType.IndesignServer.ToString())
{
dt = ds.Tables[ComponentType.IndesignServer.ToString() + "Log"];
}
else if (ctype.ToString() == ComponentType.FS.ToString())
{
dt = ds.Tables[ComponentType.FS.ToString() + "Log"];
}
}
}
catch
{
dt = null;
}
return dt;
}

DataSet.ReadXml("FilePath") directly reads the whole xml file in form of a dataset. Then, from the dataset we get our desired type of node collection if form of table. All the info node automatically created a column and their values created rows of the table. Thats how we read info from xml file.

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.