April 2007 - Posts
Let's say I have a page with some kind of image on it. Suppose we want to create a control which performs zoom on the image according to a user defined borders (User drags the mouse from one point on the image to another and creates a box). In order to do this we need to use events and scripts so we could know what area in the image the user selected.
When adding event handlers to client events in a Html or ASP.NET page you can use an object called event. Among it's properties there are a couple which refers to the position of the mouse when the event happens. When I tried to debug such control I was very confused with these properties,so I read MSDN - which doesn't explains it very well and I decided to try and explain myself.
There are four sets of properties :
- screenX and screenY - Returns the position of the mouse relative to the screen of the client. In computer screen the top-left corner is considered to be (0,0) and the bottom-right corner of the screen is (maxResolutionX,maxResolutionY) - for example (1280,1024). So if the mouse moves down and right these values will increase according to these axis.
- clientX and clientY - Returns the position of the mouse relative to the client area of the browser window. What is the client area ? This is the area in which the content of the web site is displayed (see the picture below).
- offsetX and offsetY - Returns the position of the mouse relative to the position of the object which triggered the event. That means (offsetX,offsetY) = (0,0) is in the top-left position of the object on which the event handler was defined (In the picture below there is a mousemove event on the green div so (0,0) will be at the top-left corner of this div.
- x and y - This is the most complicated one. If the object which triggered the event is relatively positioned (has position:relative in it's style) then (0,0) will be at the top-left corner of the object (Like the purple div in the image below which is defined as positioned relative). If the object is not relatively position then (0,0) will be in the top-left corner of the closest parent of the object which is relatively positioned (that means that is the parent of the object is relatively positioned then (0,0) will be at the parent's top-left corner). If none of the parents of the object throw out the hierarchy is relatively positioned then (0,0) will be the same as ClientX/Y.
Like they say - "A picture worth a thousand words" :

In the image you can see all the (0,0) points of each position property.
The image is taken from an example page I've written, in which I've defined an onmouemove event on two divs. The difference between them is that the purple div is relatively positioned. Here's the code for the example :
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="MousePositions.aspx.cs" Inherits="MousePositions" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<script type="text/javascript" language="javascript">
function mouseMoved(displayControlId) {
var clientPosition = 'Client : ' + event.clientX + ',' + event.clientY;
var offsetPosition = 'Offset : ' + event.offsetX + ',' + event.offsetY;
var screenPosition = 'Screen : ' + event.screenX + ',' + event.screenY;
var relativePosition = 'Relative : ' + event.x + ',' + event.y;
document.getElementById(displayControlId).innerText = screenPosition + ' | ' + offsetPosition + ' | ' + clientPosition + ' | ' + relativePosition;
}
</script>
<form id="form1" runat="server">
<div style="border-style:solid;border-width:thin;border-color:Blue;position:absolute;left:100px;top:50px;width:400px;height:300px;overflow:hidden">
<asp:Label runat="server" Text="Blue Div" ForeColor="Blue" ID="Label1"></asp:Label>
<div style="position:absolute;border:solid thin Red;left:10px;top:50px;width:200px;height:150px;overflow:auto">
<asp:Label runat="server" Text="Scroll Div" ForeColor="Red" ID="Label2"></asp:Label>
<div style="background-color:Purple;position:relative;left:10px;top:0px;width:100px;height:100px" onmousemove="mouseMoved('<%=Label4.ClientID%>')"></div>
<div style="position:absolute;left:40px;top:50px;width:500px;height:300px;overflow:visible;background-color:Green" onmousemove="mouseMoved('<%=Label3.ClientID%>')">
</div>
</div>
</div>
<asp:Label runat="server" ForeColor="Green" ID="Label3" Text="Green"></asp:Label>
<div style="position:absolute;left:0px;top:500px">
<asp:Label runat="server" ForeColor="Purple" ID="Label4" Text="Purple"></asp:Label>
</div>
</form>
</body>
</html>
Another thing you need to notice : Only the offset properties gives the real position when there is a scroll. For example : If you move the scroll in the example by 10 pixels then offset position will move by 10 pixels (in oppose to the other properties which will only give you the absolute position in the page). This has an exception : If the object is relatively positioned then the x,y properties will also given you the real position.
That's it. Hope this helps you.
One of the tools provided with Visual Studio 2005 is the Script Explorer. This tool can be used to view all the JavaScript that have been loaded so far, accessing the functions and objects and debug the script simply by using breakpoints.
You can use it when you are debug mode in Visual Studio :
The explorer looks like this :
You can see the pages that have been loaded so far and the script files.Here you can see I have a JavaScript file named "testscripts.js" that already have been loaded.
Clicking on the script will display it in VS were you can set breakpoints on the code and debug it.
In order to debug scripts that are written in the page itself you need to click the page in which you want to debug in the script explorer (NOT the solution explorer). The page that will be open is the page after the server parsing. Then you simply add a breakpoint on the line of script you want to debug.
Here's a short explanation by MSDN :
http://msdn2.microsoft.com/en-us/library/7seh8d72(VS.80).aspx
Remember : Before you can debug scripts you need to clear the "disable script debugging" checkbox under your browser (In IE : "Internet Options" -> Advanced -> Browsing -> Disable script debugging (Internet explorer).
Have fun.
Suppose you a have a text box in an ASP.NET page. This text box needs to contains calculated values from other inputs that will be received from the user (other text boxes). The user can not change the text in the text box directly. How do you implement that ? By using the ReadOnly property in the text box.
That's what one of the programmers on my team did a long time a go. Recently a bug in his page was found : After a post-back the values in the read-only text box have been reset, which is a problem because the JavaScript code in the page rely on the values of this text box to perform calculations - which after the post back have been incorrect. One of my team mates asked me to help him to solve this bug.He already set on it for hours , debugging each JavaScript event and each line of code in the CS file in order to find where the value is being reset.He found that all through the client script flow the value of the text box stays correct, but at the first line in the server code the value has been already reset. We debugged the code together for a couple of hours but couldn't found the problem.
In the following weekend I thought about it all the time and then it came to me : The textbox is ReadOnly!!!
MSDN explains it : "The Text value of a TextBox control with the ReadOnly property set to true is sent to the server when a post-back occurs, but the server does no processing for a read-only text box. This prevents a malicious user from changing a Text value that is read-only. The value of the Text property is preserved in the view state between post-backs unless modified by server-side code."
It doesn't get any simple then that. In general the value of a ReadOnly textbox can only be changed by server code.
So how to solve the bug ?
There are two ways :
- Recalculate the value of the textbox in the server after a post-back and set it there. The problem in this is duplicated code - The same calculation is performed in a script and in the server code - so any future modification in the calculations need to be done in both.
- Use the readOnly attribute of the input tag. This prevents the user from manually change the value of the textbox and the value is still sent to the server. To do this you need to run the following code :
TextBox1.Attributes.Add("readOnly", "true");
The problem with this is the value can be changed by the client - By changing the request (Using Fiddler) or running a javascript command through the address bar in the browser.
Here's a code example for the problem and the second solution :
Html code :
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Untitled Page</title>
<script language="javascript" type="text/javascript">
function changeData() {
var readOnlyData = document.getElementById("<%=ReadOnlyData.ClientID%>");
readOnlyData.value = parseInt(readOnlyData.value) + 1;
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="ReadOnlyData" runat="server" Text="0"></asp:TextBox>
<input id="ChangeData" type="button" value="Change" onclick="changeData()" />
<asp:Button ID="DoPostBack" runat="server" Text="PostBack" />
</div>
</form>
</body>
</html>
Code behind :
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
// The problem : The text box is set as read only :
//ReadOnlyData.ReadOnly = true;
// The solution : We replaced it with the readOnly javascript attribute :
ReadOnlyData.Attributes.Add("readOnly", "true");
}
}
}
There are :
- TextBox - which contains the value.
- Input button - which changes the value of the text box - increments it's value by one.
- ASP.Net button - which simply perform a post-back.
Try to run it with and without the commented line which sets the ReadOnly to true, and see how the value of the textbox is being reset after a post-back.
The lesson that I've learned after solving this bug : Sometimes the most strange and complex problems have a simple solution.
Also I think using a relying on a calculating value from the client is a bad practice. The page that is presented to the user is the UI. It is not suppose to perform calculations - this is the server's code job. So if I had to rewrite the page I would choose the first solution.
In our latest project we encountered a problem with the usage of a Web Service.
The web method we created received an object as a parameter that contained information about the operation we wanted to perform. When I created the web reference to the service a strange thing has happened : Visual Studio created a proxy class for the web service and another proxy class for the type of the object that the web method received. It even became more complicated when the object contained an enum : VS created a proxy for the enum too.
Why this happens ?
Web services are supposed to be used with out the user deploying assemblies on his computer. That means that the user needs all of the object that can be used with the web service without recreating them manually or deploying an assembly. So when a web method receives an object as a parameter or has an object has a return value, VS creates a proxy class which contains all the properties and fields,so the user can to create this object (which is generated under the web reference namespace) and use it in the call for the web method. Complicated ? not so much... here's a simple example :
I created a class :
namespace YsA.Testers.Entities
{
public enum MyEnum
{
FirstVal = 100,
SecondVal = 102,
ThirdVal = 103
}
public class MyObject
{
private MyEnum enumField;
public MyEnum EnumField
{
get { return enumField; }
set { enumField = value; }
}
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
public MyObject()
{
enumField = MyEnum.FirstVal;
name = String.Empty;
}
public MyObject(MyEnum e,string name)
{
enumField = e;
this.name = name;
}
public override string ToString()
{
return "Name : " + Name + "," + " Enum : " + EnumField.ToString();
}
public void DoSomething()
{
Name = "YsA";
}
}
}
I created a web service which uses the class :
using System;
using System.Data;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.ComponentModel;
using YsA.Testers.Entities;
namespace WebServicesTester
{
/// <summary>
/// Summary description for Service1
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
public class TestObject : System.Web.Services.WebService
{
[WebMethod]
public MyObject GetObject(MyEnum e,string name)
{
return new MyObject(e, name);
}
[WebMethod]
public MyEnum GetEnum(MyObject o)
{
return o.EnumField;
}
}
}
I created a web reference using Visual Studio (Right click on the project -> Add web reference).
Finally I created a class which uses the service :
using System;
using System.Collections.Generic;
using System.Text;
using YsA.Testers.WebServiceTesters.localhost;
namespace YsA.Testers.WebServiceTesters
{
class Program
{
static void Main(string[] args)
{
try
{
TestObject t = new TestObject();
MyObject o = t.GetObject(MyEnum.SecondVal, "Test");
Console.WriteLine(o.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
Notice that I'm using MyObject which is under the namespace "YsA.Testers.WebServiceTesters.localhost", which is the namespace that was created by VS and contains all the proxy class for using the web service.
Also notice that the methods in MyObject were not generated, so when we will run this code we will receive : "YsA.Testers.WebServiceTesters.localhost.MyObject" and not what the overridden ToString() method actually does in the original MyObject.
Another thing : If we print the value of "MyEnum.SecondVal" what we will get ? The result will be '1' and not '102' as defined in the original enum. This is because proxy enums are generated without there original values :
namespace YsA.Testers.WebServiceTesters.localhost
{
public enum MyEnum
{
FirstVal,
SecondVal,
ThirdVal
}
}
Now for the problem : What if we do want to use the original objects and not the proxy objects that are generated by VS ? Is it possible ?
We would like to do this when there is an assembly that is used together with the web service. For example : We have a solution with a web site and a windows application, and we want to invoke things in the web site (like clearing the cache) when certain actions are preformed in the application. The simplest way to do this is be using a web service that is defined in the web site and can perform actions on the web site (because it is under the web site it can access the application and cache of the web site). In this case we might have a class which is used in the application and web site and we want to pass an instance in the web method.
To do this we need to use a little hack...
When we create a web reference a folder is created under the project's folder named "Web References\[Web reference name (entered by the user when creating the reference)] ". Under this folder Visual Studio generates a couple of files. One of them is called "Reference.cs". This file contains the C# code for the definitions of the proxy classes. Here's the example for the MyObject & MyEnum proxy classes :
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.312")]
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://tempuri.org/")]
public enum MyEnum {
/// <remarks/>
FirstVal,
/// <remarks/>
SecondVal,
/// <remarks/>
ThirdVal,
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.312")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://tempuri.org/")]
public partial class MyObject {
private MyEnum enumFieldField;
private string nameField;
/// <remarks/>
public MyEnum EnumField {
get {
return this.enumFieldField;
}
set {
this.enumFieldField = value;
}
}
/// <remarks/>
public string Name {
get {
return this.nameField;
}
set {
this.nameField = value;
}
}
}
As you can see the enum is defined without values and the object is defined without methods - only fields and properties.
So what you need to do in order to use the original objects ?
- Erase the definition of the object's proxy classes from the reference.cs file : (In this case : MyObject & MyEnum).
- Add a reference for the assembly which contains the original objects to the project which will use the web service.
- Add a Using statement in the reference.cs class for the namespace which contains the original objects.
- Save the reference.cs file.
That's it.
Now if we run the program :
using System;
using System.Collections.Generic;
using System.Text;
using YsA.Testers.WebServiceTesters.localhost;
using YsA.Testers.Entities;
namespace YsA.Testers.WebServiceTesters
{
class Program
{
static void Main(string[] args)
{
try
{
TestObject t = new TestObject();
MyObject o = t.GetObject(MyEnum.SecondVal, "Test");
Console.WriteLine(o.ToString());
Console.WriteLine((int)MyEnum.SecondVal);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
The Results will be :
Name : Test, Enum : SecondVal
102
Pretty nice...
After you do this remember : When you refresh the web reference the proxy class will be regenerated (which will cause a few compilation errors) - refreshing simply means that requerying the server and recreating the reference , so every time you refresh you will need to delete them again.
I will be glad to here what you think about this solution , and even more if you can find another way to to this (believe me, I looked for some time).
Hi
My name is Yossi Shmueli.
I'm a programmer in a computer company in Israel.
I'm part of a team which specializes in developing GIS applications with the .Net framework, where we mainly use ESRI products.
My interests are :
- GAT/GAX : Guidance Automation Toolkit / Extentions.
- .Net and C#
- Oracle database.
- SDE : ESRI's Spatial Database Environment.
- UI
- Computer graphics & Image processing.
- Computer games.
Enjoy reading my blog...