Friday, September 26, 2008

iPone Application: MoveMe (continued)

Finally, I finished my build of MoveMe application from scratch. That's the first demo application for beginner developers. I thought it is better if I build this application by myself. I added one piece by one piece. That's quite good experience. Since Object-C is new for me, it takes some time to gain experience.

Xcode is Microsfot VS like IDE. It is hard to compare two. My first impression is good. IB (Interface Builder) is something like .Net's new Expression Blend. As its name description, it is use for UI design and link UI components, actions(for events) to class codes. At first, I did not have any clue how to use it. I thought these connections or linking must be done through IB's UI such as property, tab or dropdown list. However, when I tried to link MoveMeView class to a component in IB's outlet, I was lost. Finally, by watching some YouTube demos on IB, I figured out the linking is done through drag with Control key and drop. It's quite innovative way. I have to understand IB or Mac's way to do it.

The second lesson I learned is that parameter names in a method has to be matched if it is a delegate method. For example, when I typed in the codes for MoveMeView.m codes, I missed i in the animation delegate method's parameter name "finished":

-(void) animationDidStop:(CAAnimation *) theAnimation finshed:(BOOL) flag

As a result, the animation of MoveMe button only works at the start and then no respond after I moved it. Finally, I found the mis-typing error. It is the signature of method not matched. In Objective-C, you can call a method on object that may not defined. It would not generate an error or cause compiling error. It is OK. That's very nice. However, it may cause some very hard find bug. After I corrected my mistake, the application finds it's matched animation delegate method, and works fine. The bug caused my animation never stopping.

Another good lesson I learned is the Frameworks in Xcode project. It is similar .Net project's References. Since I created a new iPhone project in Xcode from scratch. Xcode did not add animation framework class QuartzCore.framework, which is used in MoveMe example project. I typed in codes in MoveMeView.m file. It includes the QuartzCore's header files like this:

#import <QuartzCore/QuartzCore.h>

and when I typed animation class names and methods, Xcode does show them in intellisense. However, when I compile the application. I got some errors about references not found in xxx.o. I was confused and there was not further indication where in the codes where the error is. Eventually, I realized that it is the library files missing. I have to include animation's library into the build file. In Xcode, all the libraries are under Frameworks folder in the project.

It has been very productive period for me. I got my built MoveMe working. There are some similarities between Objective-C and .Net C#. With my knowledge and experience in .Net C#, it is not so hard to understand Objective-C. However, Objective-C does have its unique features and power. I would like to continue to learn it and to develop applications.

Read More...

Sunday, September 21, 2008

iPone Application: MoveMe

I started to learn iPhone application development from Apple's iPhone Dev Center. The first application is MoveMe. The material on this application gives overall description on main items in the application, but it does provide hands-on information about how to build the application by steps.

I tried to create all the classes, resources files, and windows xib files from scratch. It is not so easy. XCode provides good IDE for coding, however, I could not find similar convenient ways as I have done .Net applications by VS.

I am still in the progress to get MoveMe build up and working. One thing I found out that the window xib file has to be specified in the Info.plist file (which is under Groups & Files->Resources):



I did not use MainWindow.xib file. Instead, I created MainMoveMe.xib file. When I first run it, I got crash exception. The problem was caused by this "Main nib file base name" in Info.plist.

After I got this window file, still I could not find a way to link delegate to MoveMeAppDelegate class in Interface Builder. Since I created this window file, I have link windows to classes through Interface Builder. Anyway, it is quite interesting experiences to learn and develop iPhone Application. I'll keep log my experiences.

Read More...

Wednesday, September 17, 2008

iPhone, Android and Windows Mobile

It is getting very crowed in mobile phone. iPhone from Apple is mainly for hight-end users. It is very existing mobile phone. However, it has some limitations. For example, you can only run one application at one time and limited openness in iPhone SDK APIs.

Google announced Android early this year, and formed Open Handset Alliance a group of more than 30 technology and mobile companies. It is based on Java framework. It supports multi-processes and is expected to be run over a wide range of handsets from 12 keypads low-end ones to high-end ones. The first mobile phone supporting Android is coming in just a few weeks time.

Microsoft also put a lots of investment and efforts on Windows Mobile and a few mobile companies have already used it.

I think early next year we will see more interesting mobile phones with great applications available on the market!

Read More...

Tuesday, September 16, 2008

Add Scripts to Client Side Page 2

I have changed the ClientSidePage class. In the following articles, I'll use my updated class as examples. It is very easy to use the class, first use the ClientSidePage class as base class, and then use the base class method MonitorChanges() to add a web control for monitoring any changes.

For example, here is the code to add a textbox control for monitoring:

protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
base.MonitorChanges<TextBox>(MonitorChangeControl,
CONTROL_ID_MONITORCHANGECONTROL);
}
}

where MonitorChanges is a TextBox control placed on aspx page. It is directly accessible in the server-side code class, and CONTROL_ID_MONITORCHANGECONTROL is a constant defined in the server-side class. I prefer to define control ID in a region in a class so that can be be easily maintained in once place instead spread all over the places.

One change in the method is that it is a method with generic type T, where T is constrained with WebControl class. The first parameter is a web control or the parent control where a specified control can be found by control's ID. In some cases, you may place some web controls within a GridView's templates. Those controls cannot be directly accessible. You have find them by their parent container such as GridViewRow control. In this case, you can pass in the GridViewRow control.

With the method, I used another class to find a web control. This makes it easy to use. You don't need to add some codes to find the control. See my previous blog on Find a control in ASP.Net Page.

You can monitor as many web controls as you want. However, from my experiences, I found there are too many problems to directly monitor each editable web controls. Some times you don't know how many they. For example, a GridView control may be bound to a database with dynamical rows of data. As a result, it is very hard to maintain.

The second issue is you have to understand when to initialize these controls on the client side script. As I mentioned in my previous article, the ClientSidePage class registers two groups of scripts on client side page. One is the startup scripts, where the method assignInitialValuesForMonitorChanges() is called when the web page is loaded. However, some web controls may not have data values available in the first loading process if the control is bound to an object data source and the object will be created on server side codes. The control may be late postcalled back to set values. In this case, the client side array for initial values would be nulls. What I find out is that I have to call MonitorChanges at the time the control is bound to data on server side. Then I use another overloaded method to pass initial values to the client side script so that the array could be initialized with correct values.

protected void GridView_RowDataBound(object sender,
GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
GridViewRow row = e.Row;
base.MonitorChanges<CheckBox>(row,
CONTROL_ID_ISEMPLOYEECONTROL, true);
}
}

What I find out is that I don't need to monitor each web controls. What I need is to monitor only one TextBox control. It is initialized as empty. If any other control values are changed, this monitored control's value will be something like "Some data may have been changed. Click on Save button to confirm the changes".

In order to achieve this, I added two methods on ClientSidePage class. The first one is SetScriptOnClientSide(). This method will register two groups of scripts to client side in the same way as CliendSidePage does: a group script for starup and a group script for script block. These two groups of scripts are customized scripts with a customer provided registry key. This will make sure ClientSidePage registered scripts will not be conflict with customized scripts. You add whatever scripts you want to the client side.

SetControlEventScript(). This method takes three parameters, one for WelControl object, a string for an event attribute, a string for script, and a position to indicate where to add the script to the controls' event attribute if it has some scripts defined already.

Here is one example I use these methods in one aspx web page server side codes:

private void SetScriptsForClientSidePage()
{
string script;
/* Monitor one text box control: MonitorChangeControl with its control ID*/
base.MonitorChanges<TextBox>(MonitorChangeControl,
CONTROL_ID_MONITORCHANGECONTROL);
script = @"
<script language=""JavaScript"">
function {0}
{
var v_elem = document.getElementById('{1}');
if ( v_elem )
{
v_elem.value = '{2}';
}
}
</script>
"
.Replace("{0}", SCRIPT_FUNC_SomeValueChanged) /* constant for function name: "SOmeValueChaned()" */
.Replace("{1}", MonitorChangeControl.ClientID)
.Replace("{2}", SCRIPT_CHANGE_MESSAGE); /* Message like "Some data may have been changed. Click on Save button to confirm changes or click on Cancel to revoke changes." */
base.SetScriptOnClientSide("script", null, script);

/*Add scripts for one text box control: CurrentSales */
script = string.Format("{0};",
SCRIPT_FUNC_SomeValueChanged);
string eventOnChange = "onchange";
ScriptInsertPosition pos =
ScriptInsertPosition.BeforeExistingScrit;
base.SetControlEventScript(CurrentSales,
eventOnChange, script, pos);
}

This private method is called in the Page_Load() event when the page is first time loaded: monitoring one text box control, adding client side script with a script function definition, and adding event script for a web control. After done this, I always to view the client side source to verify my scripts.

The next article will provide the codes of ClientSidePage class.

Read More...

Monday, September 15, 2008

Add Scripts to Client Side Page 1

I have read an excellent article on how to add client side scripts to monitor changes and prompt user if a redirection call is fired by Scott Mitchell. He later updated the class to disable prompt in case there is no need such as Save and leaving the page.

I have used this class in my project. I found there are some features missing and then further updated the class. In this article and later on articles, I'll discuss how to use this class.

Basically, the class ClientSidePage is used as a base class for an ASP.Net Web Page (server side codes). You can use this class to monitor changes made by client on the page for editable web controls by the method of MonitorChanges().

Here is the way how it works. This method will register two script array variables (page level) on client side, one for monitored control's IDs and another for initial values for those controls.

In order to make this to work, the ClientSidePage class registers two groups of scripts on the client side: startup script and script block. The startup script is a group of scripts that will be called when the client side web page is loaded. This is for initialization purpose, for example, setting the monitored array with initial values by a script function assignInitialValuesForMonitorChanges(), which is defined in the second script group. This function initializes the array of monitored values as original values, and set a page level variable, m_needToConfirm, to true. This is a flag for confirming changes.

The second group of script, script block, contains some client side function declarations, page level events, and variables such as variable m_needToConfirm, and function assignInitialValuesForMonitorChanges() as mentioned above. Another example is to set page event window.onbeforeunload is set to a script function confirmClose(). When a unload page request is made on client side, the window.onbeforeunload event is called. The function confirmClose() checks if the flag variable is set and any monitored control' values are changed or not by comparing control's value to the original values. If so, a prompt is displayed with a message to confirm leaving the page with two options: OK for leaving the page, and Cancel for staying.

By the way, in Google's Chrome beta version browser, the OK button is "Leave this page" and "Stay on this page" for Cancel.

Most browsers have view source option. You can view those groups of scripts by this option. I like Chrome's viewing source. It opens a new tab with page's source codes, and it is very nice to search. For example, you can search for the variables, arrays, or functions as mentioned above. The searched strings are highlighted with yellow color and there are marks on scroll bar to indicate their positions. You can easily location them and know if they exist right away. Maybe that's Google's strength: search everywhere and know where they are.

There are some cases that the prompt are not desired. For example, a user makes some changes on a page, and then click on a Save button to save changes. Normally the saving request is a postback call to the server side, where changes are saved and new/refreshed data are sent back to the client side. In this case, the prompt is not needed. Scott updated the class to handle this case. Read his articles for details.

Read More...

Saturday, September 13, 2008

Mac Software Development

I have have done my software development mainly in Windows Platform, some in Unix long time ago. I have got an iMac in March this year. Since then I spend a lot of my time using it at home. I really enjoy it. Recently I have installed XCode on my Mac and watched training videos about iPhone Application Development API kit.

It is very interesting to learn Objective-C and Cocoa framework even they are very different from .Net languages and framework. iMac computer is very easy to use and very stable. From what I have seen from Apple's training materials, the Objective-C and Cocoa are also very straight forward in syntax. Objective-C for Mac applications uses MVC pattern. That's the one I have seen on Microsoft's P&P team as well. I think there are a lots similarities between both.

I'll spend some time to learn Mac application development. Maybe I'll get more and more involved in Mac software development. Enjoy the new adventure!

Read More...

Tuesday, September 02, 2008

Google Launches Chrome Web Browser Today

It is getting more crowded in the web browser war. I have IE that is a part of Windows, Firefox, an excellent open source browser with multi-tabs and add-ins, Safari for Mac, and others rarely used. Now Google enters the web browser competition with Chrome!

It is a positive thing for most end users for sure. I think Chrome will have some special offers for us. First, it is built based on IE but it is open source. Soon it will be available for Mac. That means that we could have alternative browser for IE in Mac with IE special features. Secondly, its infrastructure is sandbox so that each process has its own rights which mean more privacy. Another related benefit is that one process crashing would not affect others like tabs and mails.

I think the biggest thing why Google decides to have its own browser is that the current browsers have too many dependencies. Many of Google's products and great features are depending on browser and end user decisions. For example, Google released Google Gears which provides off-line browsing. That's a great feature for most end users. The browsing process would more smooth and faster. The real-time data can be updated by the back-end database store and processes when the Internet connection is available and there is need to fresh the data. However, it is an add-in. It will be up to the end user to install it. Without the installation, it would not work.

jQuery is another great feature for web browsing. It loads additional JavaScript libraries. There are already many jQuery add-ins or plug-ins available and Google shows great interest in it. However, many features will depend on end users to install them.

I guess that Chrome will install Google Gears and many other add-ins as components by default. This will make Chrome very unique and outstanding. Many advanced features will be available right away. That will shine Chrome for sure.

Welcome Chrome to the web browser world!

Read More...

Monday, September 01, 2008

jQuery

Today I read David Hayden's blog on jQuery. It is a quite interesting JavaScript library. I tried some tutorial examples and get some tastes on how to use and its initial power.

I downloaded the library jQuery-1.2.6.js source code. Then I created a test html file. I followed the instruction to save the js file in the same folder as the html file. Then I tried some example codes to hide a reference link <a...>, add a style class to the link, add a click event to the link, and some chained queries. Basically, this library provides API functions to manipulate DOM objects or elements, handle events, perform animation, and add Ajax to your web project. As David recommended, jQuary in Action is a good book for beginners.

I'll spend some time to try out and learn this great tool. Hopefully I can use this in my web projects.

Here is a training video program by a 12 years old child in google engEdu:



Here is very good reference to jQuery API libraries: Visual jQuery 1.1


50+ Amazing jQuery Examples
. Unfortunately, some links are not available. However, some are really amazing web pages.

Read More...

Sunday, August 31, 2008

Collection Filter Function

Let's continue the topic of my previous blog on .Net Generic and Value Data Type with an example of collection filter function for generic value data types. This function takes two collections, the first one as input collection and another one as filter collection. The returned collection will filter out all the items in the filter collection from the first collection. In this case, in order to filter items, the value data type makes sense when comparing equalities for all the items in a collection to an item. For reference data types, however, it is hard to tell equality or not, by reference or property members? You even can expect multiple-levels if some property members are reference types.

public IEnumerable<T> FilerCollections<T>(
IEnumerable<T> list,
IEnumerable<T> filterList)
where T: struct
{
if ( list != null )
{
foreach (T item in list)
{
bool found = false;
if (filterList != null)
{
foreach (T filterItem in filterList)
{
if (filterItem.Equals(item))
{
found = true;
break;
}
}
}
if (!found)
{
yield return item;
}
}
}
}


Here is the test program:

Test t = new Test();
IEnumerable<int> list;
List<int> list1 = new List<int>(new int [] {1, 2, 3, 4});
List<int> list2 = new List<int>(new int[] {3});
list = t.FilerCollections<int>(list1, list2);
if (list != null)
{
foreach (int item in list)
{
Console.WriteLine("item: {0}", item);
}
}
else
{
Console.WriteLine("List is empty");
}


and the result screen:

Read More...

.Net Generics and Value Data Type

I use .Net Generics a lot. The great benefit is that you can make your codes reusable even you may use it only for one data type. You may extract the generic class or method to a library to a library for reuse. Secondly, it still keeps type safe or strong type integrity. Any attempts to use wrong type will be caught at compile time instead of run time.

The generics can be specified by where clause. In most cases I use generics for reference data types, i.e., classes. If you say a generic as class, that means the generics data type is a reference type. You can use new() to further indicate the class can be instantiated by the default constructor with no parameters. If you specify the class a specified class, that means any class which inherits from the class can be used for the generics class or method.

You cannot use more than one classes in where clause, however, you can use interfaces to specify that the generic class should implement some interfaces. Then in your generic class or method, you can safely call the implemented interfaces or methods.

How about value data types such as int, string or double as examples. You can use struct in the where clause. If you want to use the nullable value data types, you can simply use ? after the generic type, such as T?.

class Test
{
public T GetValueOrDefault<T>(T? item)
where T: struct
{
return item.GetValueOrDefault();
}
public T? GetValue<T>(T? item)
where T: struct
{
if (item.HasValue)
{
return item.Value;
}
else
{
return null;
}
}
}


here are codes to call the methods:

Test t = new Test();
int? a = 2;
a = t.GetValueOrDefault<int>(a);
//t.GetValueOrDefault<Test>(t); //Error: t is not none-nullable data type
Console.WriteLine("GetValueOrDefault<int>(a) = {0}; GetValue<int>(a) = {1}",
t.GetValueOrDefault<int>(a), t.GetValue<int>(a));
a = null;
Console.WriteLine("GetValueOrDefault<int>(a) = {0}; GetValue<int>(a) = {1}",
t.GetValueOrDefault<int>(a), t.GetValue<int>(a));
Console.WriteLine("GetValueOrDefault<int>(a) = {0}; GetValue<int>(a) = {1}",
t.GetValueOrDefault<int>(3), t.GetValue<int>(3));
 


The result of the above codes in a console is:



Here is an interest posting on .Net Generics and a question about enum data type.

Read More...

Tuesday, August 26, 2008

SQL Nested SELECT Statement in FROM Clause

You can create a view by using SELECT statement. For example, a View called as AllEmployeeInfo by SQL statement: SELECT * FROM Employees. Employees is a table. That's quite simple. Then you many list all employee names hired after '2005-01-01' from the view:

SELECT FirstName, LastName
FROM AllEmployeeInfo


Is there any way to use SQL SELECT statement as a subquery directly in FROM clause? For example:

SELECT FirstName, LastName
FROM SELECT * FROM Employees


This does not work in Microsoft SQL 2005. However, if you name the subquery SELECT as an alias, you can do it. Here is the example:

SELECT FirstName, LastName
FROM (SELECT * FROM Employees
WHERE HireDate > '2005-01-01') AS Temp
-- or
SELECT FirstName, LastName
FROM (SELECT * FROM Employees) AS Temp
WHERE
HireDate > '2005-01-01'


It looks like that SQL will create a temporary or dynamic view for the alias, then you could SELECT columns from there. Quite cool? I tried this in PL/SQL Oracle 9.0 but it does not work. Anyway, I like this quick way to get data. Sometimes you may need this kind of nested SELECTs so that you can filter data by several levels and avoid to create unnecessary views.

More on nested SQL queries, read this blog: Using a Subquery in a T-SQL statement.

Notes and Updates on this blog
: Actually, the nested SELECT statement is available in Oracle or PL/SQL as well, but you don't need to AS in the statement. I got this correction from my StackOverFlow Q&A. Thanks for the correction!

Read More...

Sunday, August 24, 2008

ASP.Net and Data Source Controls

In an ASP.Net page, many asp controls can be bound with a data source control, such as SqlDataSource and ObjectDataSource. I prefer to use ObjectDataSource than SqlDataSource. Here are my two main reasons.

First SqlDataSource includes a SQL statement in the control. If you change different data DB source from Microsoft SQL to Oracle DB, the SQL statement may be different. Therefore, you may have to make change in the UI aspx page or server side .Net class codes.

ObjectDataSource is binding data source from a a class's methods to get or update data. The class hides the way how data are retrieved or saved. I call it as business logic layer class (BLL). The class can be defined in another library assembly. This will make unit test much easier.

Second reason is that you don't have control how many times to call SqlDataSource to get data. For example, if you have a combo box in a GridView for product types and link it to a SqlDataSource to get product types. Each combo box will call the SqlDataSource to get product types when GridView is initialized. If there are tens or hundreds of rows in the GridView, there will be tens or hundreds call to database to get the same data. Even worse, in case of post-back calls, the GridView will be refreshed again. This may make the front UI very slow if the retrieving data process is slow.

In BLL class, you can easily cache data to avoid unnecessary calls to database. Simple define your data collection as a static List in the class. Only the first time when the page is initialized the static data member is populated with data from database. The subsequent calls will get data from the cache. You may need to define a reset method in the BLL class so that in the Page_Load event, if it is not post-back call, call this reset method to reset the static member to null. Then when the GridView is initialized, the data will be obtained from database for the first time.

Read More...

Tuesday, August 12, 2008

Using Query String Parameters in ASP.Net Page

You can use query string parameters in a URL request string to pass information into an aspx page. This is very common way to pass values from one page to another page. The URL query string is in the format like:

http://mypage.aspx?para1=value1¶2=value2...


I also use this mechanism to pass in additional data or flags to display some hidden values or to provide additional information from server side codes. For example, in a GridView control, normally we only display a UI table which are readable by clients, such as employee first name, last name and department. The employee ID, which is important information for server side updating, is normally a hidden column in the table.

As in this example, my codes handle some query parameters. One is "displayEmployeeID". If it is true, the employee ID then is visible:

http://mypage.aspx?para1=value1¶2=value2&displayEmployeeID=true


This parameter can be manually added the URL address text box (use & to separate parameters). If it is not available, the default value is false. Here are some codes in my aspx.cs page in the Page_Load() event:

bool display = false;
string sVal = HttpUtility.UrlDecode(Request.QueryString["displayEmployeeID"]);
if (string.IsNullOrEmpty(sVal) == false)
{
if (!bool.TryParse(sVal, out display))
{
display = false;
}
}
DataGridView.Columns[0].Visible = display;


I use the same way to display my business logic layer class's SQL string on my page so that it makes my debugging work very easy. I add a panel to the page in aspx:

<asp:Panel ID="panelDebug" runat="server" Visible="false" width="720px">
<table width="100%"><tr align="left" ><td style="width: 110px" >
<asp:Label ID="lblSQL" runat="server" Text="Create Object SQLs: " /></td><td>
<asp:TextBox ID="txtSQL" runat="server" Wrap="true" Width="600px"></asp:TextBox>
</td></tr><tr align="left"><td style="width: 25%">
<asp:Label ID="lblUpdate" runat="server" Text="Updates: " ></asp:Label></td><td>
<asp:TextBox ID="txtUpdate" runat="server" Width="600px" Wrap="true"></asp:TextBox>
</td></tr>
</table>
</asp:Panel>


Then in the event of Page_Load() and the place when my business class has retrieved data from a SQL DB:

panelDebug.Visible = GetDisplaySQL(); // method to get query parameter "displaySQL" value 
...
txtSQL.Text = bllObj.QuerySQLs; // udpate SQL by BLL class property QuerySQLs
...


Here the text box txtUpdate is as same as query SQL but for update SQL statements.

I call those query parameters as hidden parameters. They provide handy ways to get additional information about what was happened in server side, and they can also be passed in as alternative input values.

Read More...

Tuesday, August 05, 2008

ASP.Net: Add Client Side JavaScript Codes

ASP.Net controls do not support all the control events like window form application. For example, OnKeyUp, OnMouseUp, and OnMouseMove events. It make sense that if these events were supported, there would be too many calls from client side back server, and it would make aspx pages very very slow.

However, there are some cases you would like your aspx page to support these events. For example, I have a asp:TextBox control with an attribute of OnTextChanged event. This even only fires back to the server when you change the text and leave the control to any where on the same page. If you click on another link on the page right after you change the text, this event would not be called at all.

In order to catch this event and set a flag on the page to indicate a change, I have added another asp:TextBox control called MonitorChangeControl. Then I added the following codes in the Page_Load event to insert client side JavaScript function to the aspx page so that on the client side the MonitorChangeControl text will be changed to a text "Some values may have been changed." when my monitored textbox is changed:

string changeScript = "<script language='javascript'> function SomeValueChanged() {" +
"document.getElementById('" + MonitorChangeControl.ClientID +
"').value = 'Some values may have been changed.'; }</script>";
// Add the JavaScript code to the page.
if (!ClientScript.IsClientScriptBlockRegistered("SomeValueChanged"))
{
ClientScript.RegisterClientScriptBlock(this.GetType(), "SomeValueChanged", changeScript);
}


After register the script function, then in the following codes I add an attribute "OnKeyUp" with a script call to the function "SomeValueChanged()":

TextBox myTextBox = WebPageUtil.FindControlRecursive(row, "curr_day"); //row is a GridViewRow
if (currValue != null)
{
myTextBox.Attributes.Add("OnKeyUp", "return SomeValueChanged()");
}


When I run the aspx page in a browser, the TextBox is converted to an Input document element with an attribute like this:

<input name="..." OnKeyUp=""return SomeValueChanged()" .../>


This attribute will cause the client side event fired whenever the input element's text is changed or key-is-up.

So far so good. However, for an asp:CheckBox control, if the attribute is added in a the same way, the attribute is actually added to a span element in the aspx page which is outside of the input element (converted from CheckBox):

<span ... OnKeyUp="" ...><input id=... type="checkbox" .../></span>


Therefore, I have to add the attributes in a different way:

// row is a GridViewRow control and FindControlRecursive is my function.
CheckBox box = WebPageUtil.FindControlRecursive(row, "ValidatedCheckBox");
if (box != null)
{
box.InputAttributes.Add("OnKeyUp", "return SomeValueChanged()");
box.InputAttributes.Add("onmouseup", "return SomeValueChanged()");
}


After that, I tried to view source from the browser. Here is the result of what I expected:

<input id=... type="checkbox" ... OnKeyUp="return SomeValueChanged()" onmouseup="return SomeValueChanged()" />


By the way, the event of OnChange is working for TextBox control but not for CheckBox controls. The OnChange is fired only after you make a change and at the moment whey you leave the control. For complete HTML element, properties/attributes, and events, see the page of HTML Event Attributes and JavaScript Event References.

For the function FindControlRecursive(), see my previous blog on May 29, 2008

Read More...

Monday, August 04, 2008

SqlContext.Pipe.Send not Available in SQL Server Project's Functions

I tried to use SqlContext.Pipe.Send to send a message back from a SQL project's funciton. It does not work at all. When the function is called, it throw an exception at the point SqlContext.Pipe.Send is called. It works fine in a SQL project' Stored Procedure.

Normally, I use this send to debug my program in the development stage. With this limitation, I have to create a stored procedure instead. After everything is fine, I then change it back to a function.

Another related issue about SQL Server Project, if I tried rename the previous deployed stored procedure as a way to back up the previous version of CLR SP, the SP is gone after I deploy my project again. It looks like that the deployment is smart enough to remove the previous version first, no matter you rename it or not. It does make sens since the new CLR assembly will be deployed and replace the old one. Even you rename it, the CLR SP should not be able to work since the assembly has been updated.

Read More...

Thursday, July 31, 2008

Comparing Two Tables By SQL Stored Procedure

I have created a SQL Stored Procedure to compare any two tables based on Microsoft SQL Server 2005 new syntax EXCEPT and INTERCEPT. Basically, I used EXCEPT and UNION to get the result of differences between tables and INTERCEPT to get the same result of two tables.

In addition to that, the SP will compare two tables by specifying column fields and conditions and display the result by optional ORDER BY clause. Here is the SP:

CREATE procedure [dbo].[SP_CompareTables] (
@table1 varchar(100),
@table2 varchar(100),
@table_colList varchar(3000) = NULL,
@whereClause varchar(3000) = NULL,
@orderByClause varchar(3000) = NULL,
@difference0 int = 0
)
AS
DECLARE
@sql varchar(8000);
DECLARE @colList varchar(3000);
BEGIN
if
( @table_colList is null Or @table_colList = '' )
begin
set
@colList = '*';
end
else
begin
set
@colList = @table_colList;
end
if
( @difference0 = 0 )
begin
set
@sql =REPLACE(REPLACE(REPLACE('
SELECT ''@table1'' AS TblName, *
FROM (
SELECT @colList
FROM @table1
EXCEPT (
SELECT @colList
FROM @table2)
) x
UNION ALL
SELECT ''@table2'' AS TblName, *
FROM (
SELECT @colList
FROM @table2
EXCEPT (
SELECT @colList
FROM @table1)
) y'
,
'@table1', @table1),
'@table2', @table2),
'@colList', @colList);
end;
else
begin
set
@sql =REPLACE(REPLACE(REPLACE('
SELECT @colList
FROM @table1
INTERSECT (
SELECT @colList
FROM @table2)'
,
'@table1', @table1),
'@table2', @table2),
'@colList', @colList);
end;
if ( @whereClause is not null And len(@whereClause) > 0 )
begin
set
@sql = REPLACE(REPLACE('
SELECT * FROM (@sql) v
WHERE @whereClause'
,
'@sql', @sql),
'@whereClause', @whereClause);
end
if
( @orderByClause is not null And len(@orderByClause) > 0 )
begin
set
@sql = REPLACE(REPLACE('@sql
ORDER BY @orderByClause'
,
'@sql', @sql),
'@orderByClause', @orderByClause);
end;
print @sql;
exec(@sql);
return 0;
END


To use this SP is very simple. For example, to compare two tables of [Employees] and [Employees_backup], you can just run the following script to compare two whole tables:

EXEC SP_CompareTables 'Employees', 'Employees_backup';


More examples by specifying columns, where clause and order clause:

EXEC SP_CompareTables 'Employees', 'Employees_backup', 'FirstName, LastName';
EXEC SP_CompareTables 'Employees', 'Employees_backup', 'FirstName, LastName',
'FirstName like ''D%'' AND BirthDate Between ''Jan 01, 1990'' AND ''Jul 30, 2008''';
EXEC SP_CompareTables 'Employees', 'Employees_backup', 'FirstName, LastName',
NULL, 'FirstName, BirthDate' , 1; -- get same results

Read More...

Saturday, July 26, 2008

SQL Server Project (4)

I have to close this series articles on SQL Server Project. The final part will cover some special issues related to SQL Server Project.

The first issue is the connection to SQL server. As I mentioned in SQL Server Project (2), it is recommended to use the context connection since the CLR assembly is already in a SQL server running process. However, this connection can only be created once. You cannot create another context connection for other executions. Normally, you don't need another one in one stored procedure for example. However, if you create several SQL procedures, functions, and triggers in one dll, you might get exceptions if one calls another since only one context connection is allowed. You don't have control who is going to run these stored procedures, functions, or triggers.

Therefore, I think it is better to create one SQL item (SP, function or trigger) in one dll. You could create a normal SQL connection with catalog for a db table, user name, and password information in a connection string if you have to, and that connections can be created more than once. In a SQL server context, it does not make sense to do that unless you need to connect to another SQL server or Oracle server.

An related issue is that always to handle exceptions in your assembly and close any opened connections. As I mentioned before, the assembly is loaded to SQL server running process, and it would not unloaded automatically when your SP exits. The assembly may still in memory. If you don't handle exceptions, the opened connection will block the same SP being called again.

The second issue is that some assemblies may not work in SQL server project. I tried NHibernate and some other dlls as my references. What I found is that some assembly reflection functions are not working in SQL server. For example, I found that there always exceptions when these dlls try to load another assembly file to get class or property information. All these kind calls cause exceptions. This is very unfortunate and I think this is very bad limitation for SQL server project. I have no idea why and how exceptions would happen.

If you are going to write your SQL server project in C#, all the parameters in a SP have to be specified whey the SP is called, unlike MS Transact SQL SP could have default values for parameters.

The installation and deployment process for a SQL Server Project is a complicated one. As I mentioned in my previous articles, some asymmetric keys, log in permissions have to be created, and dependency assemblies have to be registered. The deployment of SQL server project is one click process if you have source codes and Visual Studio avalailbe. However, that click-only-once deployment may hide some SQL calls to register and set up all SPs, function and triggers. If you want to create an automation process in SQL, you have to keep all the assembly files available somewhere even they are not referenced after the installation and deployment. To uninstall it, the process is reverse. You have to remove all the SPs, functions, and triggers first, and then to delete assemblies, and then other dependency items. Since there are many things involved, anything wrong may cause your assembly not being functional.

I had a case that my CLR assembly SP did not work one day. It says about some permissions to load assembly failure. I tried to remove all the assembly and to re-install again, but I could not remove them as well. I was stuck in the middle. Finally I found it is the case SQL server was in low virtual memory. The error message actually was misleading. I restarted the SQL sever and reloaded files I did in the middle, then everything worked fine. Therefore, be prepared to handle all the uncertainties, document all the procedures in a well organized way and save all the source codes and dependency files in repository.

Read More...

Monday, June 09, 2008

SVN, Its Tools and Checkins

Subversion (SVN) is an open source version control system. I used it for source code control or repository. There are two open source SVN tools available for Windows and Visual Studio: TortoiseSVN and AnkhSVN.

Normally, I use Tortoise to check out source codes, a solution or project, to a folder and then it will create SVN client folders and files (hidden) to mark files as a copy out from a SVN reposity. Then, open the sln or project by VS. AnknSVN will detect this project is from SVN and prompt you AnkhSVN is available for use for the project.

If you make a change to a file, the file then is marked as a changed one with red icon. You can commit the change back to the SVN server or check in to the repository. Ankhn works well for editing files. However, it does not support file renaming. You have to remove the file and add a new one as two steps for renaming.

In many cases, I have to revert some changes or the whole changes by using Ankhn's context menu. Revert means no change to the file in local and re-copy the file from repository to local. In case of removing a file, you will be prompt to remove the file from the repository. If you choose Yes, this actually is commit or change to the repository! You cannot just revert the change to get it back. You have to revert to previous versions. If you have several check-ins in a day, it may be hard to find the correct one restored back. Therefore, if you want to make a trial change in local, do not remove the file from the repository. Then you can simply revert the local project back and use Update to refresh the local files if you do not want to commit the change.

If you removed file from local (not in the repository!), and verified the change is OK and want to commit you changes to the repository. Remember, the removed files are still in the repository after your check-in. Use Tortoise's repo-browser to open the repository and remove the file from the browser. That's another commit change to the repository.

In a sense, those SVN tools do not have a check out session, or SVN does not support check out set or locking a check out set. That means several developers can work on a same source code file at same time. Any developer can drop their change and that's fine for others. However, if one checks in the code file, others may have to merge their changes before check in. Ankh provides diff for comparing repository file to local files and will prompt, I think, you if your check out version has been changed by others.

Read More...

Wednesday, June 04, 2008

Add Colors to Codes

Today I did some change to my blog's template html page with some style sheet definitions from ASP.Net forums' colrcode.css.

To find the css file, you have to view its source codes and find link to css at the top of web page. There are several css files. To find the style definitions for programming codes, you can view the partial codes from the forum web page's program codes.

ASP.Net forums provide on-line posting editor with a tool bar to insert source codes with formatted HTML tags around programming source codes, such as C#, VB, SQL, XML, ASP.Net and others. That's why I choose ASP.Net forums' css. I can use its posting editor to get some formatted HTML codes and then post them to my blog. Since I use the same css file in my template, my source codes are marked with colors!

One color is missing in ASP.NET page: color for class, interface and intrinsic or predefined types. I added a new tag as classInterface in my template for these cases. You will see my example codes with colors!

Read More...

Calculate Number of Months from a Date

Here is a simple function to calculate number of months from a date to now:

public int NumberOfMonths(DateTime fromDate)
{
int months;
DateTime dt = DateTime.Now;
months = dt.Month + dt.Year * 12 -
(fromDate.Month + fromDate.Year * 12);
return months;
}


It is simple and no need to add any comments. However, when I first tried to google it, I found some very complex codes to get number of months and they are not right. Finally, based those codes, I figured out that the calculation is simple.

You may use this function twice to get difference between two dates.

One note about this function is that if the input date value is a future time, the number of months is a negative value.

Read More...