BradVin's .Net Blog

Code, snippets, controls, utils, etc. Basically all things .net
(Better) JQuery IntelliSense in VS2008

I know, I know, this has been done before. The reason I'm writing this post, however, is to improve on what I have found on the web. For anyone wondering what jQuery is, goto the jQuery site and also read this tutorial. Now onto the good stuff....

If you are like me and you've read the many articles about how to get other javascript libraries to work in VS2008, you'll know that all you really need to do is install the visual studio HOTFIX. This patches your VS and among other things, gets the javascript intellisense working nicely.

Before the hotfix:


After the hotfix:

You'll notice you can now use the jQuery $ shortcut function.

At this point I was delighted. and then I typed $(document). and I got no more intellisense, why???!!! After looking around some more, I found an article on the topic written by Rick Strahl : http://www.west-wind.com/WebLog/posts/251271.aspx. It helped explain things quite a bit and I recommend you read it. Basically, if you use the 'new' keyword in declaring jQuery objects, you get intellisense, so the following works nicely and you get intellisense:

var doc = new jQuery(document);
doc.ready(function() {
var div = new jQuery("#someDiv");
div.html("some text here!");
div.fadeOut(5000);
});

But I still wasn't satisfied. Why should I change the way I write my jQuery just so VS understands it? Yes I get intellisense, but I'm writing more code, and it's unnecessary code at that. Remember : jQuery is the "write less, do more, JavaScript library".

Then onto another point: what about one of the most powerful aspects of the jQuery architecture - the chainability? The following code

$("a").addClass("test").show().html("foo");
doesnt give you any intellisense whatsoever. In order to get intellisense, it has to be written as follows:
var a = new jQuery("a");
a.addClass("test");
a.show();
a.html("foo");

So intellisense for jQuery at this point is actually quite useless I thought. Because in order to use it, we have to write double the amount of code, and jQuery's chainability power isnt being utilised at all. :(
Another bummer was the fact that the method info for the jQuery functions was seriously lacking. Obviously this is because the jQuery library has been written to be as small as possible. But it would be nice if the functions were explained together with the paramaters (exactly like we are used to when writing c# code).

so back to google and back to searching for alternatives...
This is when I found this gem of an article written by James Hart : http://blogs.ipona.com/james/archive/2008/02/15/JQuery-IntelliSense-in-Visual-Studio-2008.aspx .  And it literally solved all my problems. Here is what the method info was before:

And this is how it looks afterwards:

Big improvement! And here is the intellisense in action (using chainability):

Awesome!!!! But how does it work? Basically, a new javascript is generated and referenced from the page. This new file is just a list of methods with no functionality and it 'tricks' VS into producing nice intellisense. It also uses XML documentation to provide useful parameter listings and return types. (You can read more about javascript documentation comments here: http://weblogs.asp.net/bleroy/archive/2007/04/23/the-format-for-javascript-doc-comments.aspx ). For ASPX pages, all you need to do is include the script inside an invisible placeholder :

<script type="text/javascript" src="jQuery.js" ></script>
<asp:PlaceHolder runat="server" visible="false">
<script type="text/javascript" src="JQuery.Intellisense.js" ></script>
</asp:PlaceHolder>
This is to stop the file from actually being included in the rendered page. Note that it must also be after the reference to the 'real' jQuery file.
And in other external .JS files that use jQuery code, add the following to the top of the file:
/// <reference path="jquery.intellisense.js"/>

So now I can write my jQuery code as I did before and I have excellent method information and intellisense. This works fine in external javascript files, plain ASPX pages, master pages, content pages and user controls.

So are there still some issues? Yes there are. Firstly the generated file is using an outdated jQuery version 1.2.1 and even more outdated jQuery documentation version 1.1.2. This is by no means the fault of the author, but rather the jQuery team who have not updated the XML documentation. Secondly, certain functions in jQuery have optional parameters and return different types based on those parameters e.g. the html() function. These functions unfortunately don't give me intellisense because the generator gives them a return type of "Object" in the XML comments. No problem, just edit the generated file and change it where it has <returns type="Object"></returns> to <returns type="jQuery"></returns>
A list of the functions I found that need changing are :

  • css
  • html
  • attr
  • text
  • val
  • height
  • width

Ok, so stricly speaking, it's not correct, because these functions will return a non jQuery object when nothing is passed in, but what the hell. I still get more intellisense than before. And besides, you must at least have some idea of what the functions do, and not rely on intellisense exclusively to write code ;)

You can download my JQuery.Intellisense file with the changes already made. It is using API version: 1.2.3 and Documentation version: 1.2.2.

jQuery Intellisense In 3 Easy Steps

  1. Install VS2008 hotfix
  2. Generate a JQuery.Intellisense.js stub file. Either goto http://www.infobasis.com/sandpit/jQuery-Intellisense/ and generate it, or download a more up to date one (including minor fixes) from here. save it to the same folder as your jQuery file
  3. Add a invisible placeholder into your header and reference the new .js file:
        <asp:PlaceHolder runat="server" visible="false">
            <script type="text/javascript" src="JQuery.Intellisense.js" ></script>
        </asp:PlaceHolder>
    or add a referenece at the top of your .js file
    /// <reference path="jquery.intellisense.js"/>

Download the whole website: JqueryIntellisenseWebsite.zip

Code samples:

simple jQuery page with full intellisense:

<%@ 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 runat="server">
<title>I have intellisense!</title>
<script type="text/javascript" src="jQuery.js" ></script>
<asp:PlaceHolder runat="server" visible="false">
<script type="text/javascript" src="JQuery.Intellisense.js" ></script>
</asp:PlaceHolder>

<script type="text/javascript">
$(function() {
$("#someDiv").hide().html("Some text here!!!").addClass("bold").fadeIn(1000).fadeOut(2000);
});
</script>

<style type="text/css">
.bold{ font-weight:bold; }
</style>
</head>
<body>
<form id="form1" runat="server">
<div id="someDiv">
</div>
</form>
</body>
</html>


simple master page :
<%@ Master Language="C#" AutoEventWireup="true" CodeFile="MasterPage.master.cs" Inherits="MasterPage" %>
<!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>
<script type="text/javascript" src="jQuery.js" ></script>
<asp:PlaceHolder runat="server" visible="false">
<script type="text/javascript" src="JQuery.Intellisense.js" ></script>
</asp:PlaceHolder>
<asp:ContentPlaceHolder id="head" runat="server">
</asp:ContentPlaceHolder>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
</div>
</form>
</body>
</html>


simple content page :
<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" CodeFile="Default3.aspx.cs" Inherits="Default3" Title="Untitled 

Page" %>


<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
<script type="text/javascript">
$(function() {
$("#someDiv").addClass("bold").fadeIn(2000).fadeOut(1000).html("gone");
});
</script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
</asp:Content>


simple user control :
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="TestControl.ascx.cs" Inherits="TestControl" %>
<asp:PlaceHolder ID="PlaceHolder1" runat="server" visible="false">
<script type="text/javascript" src="JQuery.Intellisense.js" ></script>
</asp:PlaceHolder>
<script type="text/javascript">
$(function() {
$("#someDiv").hide().html("Some text here!!!").addClass("bold").fadeIn(1000).fadeOut(2000);
});
</script>
Creating vCalendars programmatically in C#

Have you ever wanted to send out meeting requests from your code? Well I wanted to do just that today. And I didnt want to reference the Outlook dll's. I simply wanted to send an email to an attendee's email address.

After much searching and alot of useless code I found a gem at http://chuckdotnet.blogspot.com. I took the code there and modified it slightly so that you can send the meeting request to multiple recipients (attendees) at once.

below is the method that creates the system.net.mailmessage object with all the necessary alternateviews, so that when opened in outlook it behaves exactly like a meeting request that was sent from within outlook. remember to add using System.Net.Mail;using System.Net.Mime; to the top of your code.


public static MailMessage CreateMeetingRequest(DateTime start, DateTime end, string subject, string summary,
string location, string organizerName, string organizerEmail, string attendeeName, string attendeeEmail)
{
MailAddressCollection col = new MailAddressCollection();
col.Add(new MailAddress(attendeeEmail, attendeeName));
return CreateMeetingRequest(start, end, subject, summary, location, organizerName, organizerEmail, col);
}

public static MailMessage CreateMeetingRequest(DateTime start, DateTime end, string subject, string summary,
string location, string organizerName, string organizerEmail, MailAddressCollection attendeeList)
{
MailMessage msg = new MailMessage();

// Set up the different mime types contained in the message
System.Net.Mime.ContentType textType = new System.Net.Mime.ContentType("text/plain");
System.Net.Mime.ContentType HTMLType = new System.Net.Mime.ContentType("text/html");
System.Net.Mime.ContentType calendarType = new System.Net.Mime.ContentType("text/calendar");

// Add parameters to the calendar header
calendarType.Parameters.Add("method", "REQUEST");
calendarType.Parameters.Add("name", "meeting.ics");

// Create message body parts
// create the Body in text format
string bodyText = "Type:Single Meeting\r\nOrganizer: {0}\r\nStart Time:{1}\r\nEnd Time:{2}\r\nTime Zone:{3}\r\nLocation: {4}\r\n\r\n*~*~*~*~*~*~*~*~*~*\r\n\r\n{5}";
bodyText = string.Format(bodyText,
organizerName,
start.ToLongDateString() + " " + start.ToLongTimeString(),
end.ToLongDateString() + " " + end.ToLongTimeString(),
System.TimeZone.CurrentTimeZone.StandardName,
location,
summary);

AlternateView textView = AlternateView.CreateAlternateViewFromString(bodyText, textType);
msg.AlternateViews.Add(textView);

//create the Body in HTML format
string bodyHTML = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\r\n<HTML>\r\n<HEAD>\r\n<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=utf-8\">\r\n<META NAME=\"Generator\" CONTENT=\"MS Exchange Server version 6.5.7652.24\">\r\n<TITLE>{0}</TITLE>\r\n</HEAD>\r\n<BODY>\r\n<!-- Converted from text/plain format -->\r\n<P><FONT SIZE=2>Type:Single Meeting<BR>\r\nOrganizer:{1}<BR>\r\nStart Time:{2}<BR>\r\nEnd Time:{3}<BR>\r\nTime Zone:{4}<BR>\r\nLocation:{5}<BR>\r\n<BR>\r\n*~*~*~*~*~*~*~*~*~*<BR>\r\n<BR>\r\n{6}<BR>\r\n</FONT>\r\n</P>\r\n\r\n</BODY>\r\n</HTML>";
bodyHTML = string.Format(bodyHTML,
summary,
organizerName,
start.ToLongDateString() + " " + start.ToLongTimeString(),
end.ToLongDateString() + " " + end.ToLongTimeString(),
System.TimeZone.CurrentTimeZone.StandardName,
location,
summary);

AlternateView HTMLView = AlternateView.CreateAlternateViewFromString(bodyHTML, HTMLType);
msg.AlternateViews.Add(HTMLView);

//create the Body in VCALENDAR format
string calDateFormat = "yyyyMMddTHHmmssZ";
string bodyCalendar = "BEGIN:VCALENDAR\r\nMETHOD:REQUEST\r\nPRODID:Microsoft CDO for Microsoft Exchange\r\nVERSION:2.0\r\nBEGIN:VTIMEZONE\r\nTZID:(GMT-06.00) Central Time (US & Canada)\r\nX-MICROSOFT-CDO-TZID:11\r\nBEGIN:STANDARD\r\nDTSTART:16010101T020000\r\nTZOFFSETFROM:-0500\r\nTZOFFSETTO:-0600\r\nRRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=11;BYDAY=1SU\r\nEND:STANDARD\r\nBEGIN:DAYLIGHT\r\nDTSTART:16010101T020000\r\nTZOFFSETFROM:-0600\r\nTZOFFSETTO:-0500\r\nRRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=2SU\r\nEND:DAYLIGHT\r\nEND:VTIMEZONE\r\nBEGIN:VEVENT\r\nDTSTAMP:{8}\r\nDTSTART:{0}\r\nSUMMARY:{7}\r\nUID:{5}\r\nATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=\"{9}\":MAILTO:{9}\r\nACTION;RSVP=TRUE;CN=\"{4}\":MAILTO:{4}\r\nORGANIZER;CN=\"{3}\":mailto:{4}\r\nLOCATION:{2}\r\nDTEND:{1}\r\nDESCRIPTION:{7}\\N\r\nSEQUENCE:1\r\nPRIORITY:5\r\nCLASS:\r\nCREATED:{8}\r\nLAST-MODIFIED:{8}\r\nSTATUS:CONFIRMED\r\nTRANSP:OPAQUE\r\nX-MICROSOFT-CDO-BUSYSTATUS:BUSY\r\nX-MICROSOFT-CDO-INSTTYPE:0\r\nX-MICROSOFT-CDO-INTENDEDSTATUS:BUSY\r\nX-MICROSOFT-CDO-ALLDAYEVENT:FALSE\r\nX-MICROSOFT-CDO-IMPORTANCE:1\r\nX-MICROSOFT-CDO-OWNERAPPTID:-1\r\nX-MICROSOFT-CDO-ATTENDEE-CRITICAL-CHANGE:{8}\r\nX-MICROSOFT-CDO-OWNER-CRITICAL-CHANGE:{8}\r\nBEGIN:VALARM\r\nACTION:DISPLAY\r\nDESCRIPTION:REMINDER\r\nTRIGGER;RELATED=START:-PT00H15M00S\r\nEND:VALARM\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n";
bodyCalendar = string.Format(bodyCalendar,
start.ToUniversalTime().ToString(calDateFormat),
end.ToUniversalTime().ToString(calDateFormat),
location,
organizerName,
organizerEmail,
Guid.NewGuid().ToString("B"),
summary,
subject,
DateTime.Now.ToUniversalTime().ToString(calDateFormat),
attendeeList.ToString());

AlternateView calendarView = AlternateView.CreateAlternateViewFromString(bodyCalendar, calendarType);
calendarView.TransferEncoding = TransferEncoding.SevenBit;
msg.AlternateViews.Add(calendarView);

// Adress the message
msg.From = new MailAddress(organizerEmail);
foreach (MailAddress attendee in attendeeList)
{
msg.To.Add(attendee);
}
msg.Subject = subject;

return msg;
}
Posted: Jan 16 2008, 08:29 PM by bradvin | with 11 comment(s)
Filed under: ,
UberUtils - Part 5 : Configuration

ÜberUtils Series posts so far :

**** Please note that this code is using .NET 3.5 ****

Now onto the post - everyone I'm sure has used the system.configuration namespace before in a project. If you haven't then shame on you. If you have, then you know it should be used for storing things like :

  • admin email address
  • connection strings to a database
  • log file path
  • Tax amounts

and so on. I am a strong believer in NEVER hard code a setting. There is just no reason to do so. What happens if a setting changes down the line? Lets use a webmaster email as an example. You would have to locate wherever you use the email address (probably more than one place) and edit it. Then recompile and redeploy your changes. What a mission! Instead, just store it in the appsettings section of the .config file and edit the setting there whenever you need to.

I really do like using the appsettings and connectionstring sections in the .config files, but I think there is room for improvement. Wouldn't it be nice if appsettings was strongly typed for types other than string? Indeed it would. Here's an example: you store a "Testing" appsetting that determines if your website is in testing mode. If it is in testing mode, then you send all emails to Admin instead of sending emails to the actual users. Now every time you want to check if the site is in testing mode you have to convert the appsetting to a boolean first. Similarly with a Tax appsetting. You have to convert it to decimal every time before you can use it.

This then follows directly onto my next question : Wouldn't it be nice if appsettings and connectionstrings had default values? Yes again. Let's say the "testing" appsetting we spoke about earlier wasn't in the .config file. We would then want it to default to true. Maybe our tax appsetting should default to 0.14 and maybe we always use the same default connectionstring in our data access layer. Defaults would be great.

I address these two shortfalls with Utils.Configuration. Lets look at some examples:

bool bTesting = Utils.Configuration.ConfigurationManager.AppSettings["Testing", true];
this gets the testing appsetting with a deafult of true (if we forgot to add the appsetting into the .config file). Notice too how it's strongly typed to be boolean. You could've also done it this way :
bool bTesting = Utils.Configuration.ConfigurationManager.AppSettings.GetValue("Testing", true);

This goes the same for the other types : Decimals, Ints, Doubles, Datetimes and obviously strings.

decimal nTax = Utils.Configuration.ConfigurationManager.AppSettings["Tax", 0.14M];
The same applies for connectionstrings, except the output is obviously always a string. You can also check if a connection string exists so you can provide better error handling :
if (ConfigurationManager.ConnectionStrings.Exists("TestConn2"))
{
return ConfigurationManager.ConnectionStrings["TestConn2", "Driver={SQL Server};Server=myServerAddress;Database=myDataBase;Uid=myUsername;Pwd=myPassword;"];
}
download the assembly with unit tests here
Posted: Jan 16 2008, 06:07 PM by bradvin | with 3 comment(s)
Filed under: , ,
UberUtils - Part 4 : Collections

 ÜberUtils Series posts so far :

I figured it was about time I did another post in the UberUtils series seeing that I haven't written a post in over a month. So here is the next installment. This time it's for collections. I hear you asking "but the framework has every collection I will ever need", and this is true for 98% of the time. But every once in a while there comes a time where I find myself subtly changing the way an existing collection works, or wishing it could do something just slightly differently. As with any software development, there are many different ways to solve the same problem, so you probably find yourself getting around limitations with the built-in collections without even knowing it. (I know I have when I go back and look at code I've written).

The number one question I have found myself asking over the years with regards to the collection namespace is:

Is there a dictionary whereby I can access the values from both the key and an index?

The answer to this is NO. And if I'm wrong about this then please shoot me now for wasting so much time over the years on this code. You can overcome this limitation by using the other collections at your disposal, and with not so much code either. The thing is, I hate writing the same code over and over again (I actually refuse to do it), so why not write a generic dictionary that can access the values by index? And then just use that dictionary instead of the normal dictionary in the future.

Introducing the IndexedDictionary, an indexable dictionary. Here is a unit test to show the indexing feature :

        [TestMethod()]
public void SomeTest()
{
IndexedDictionary<string, int> col = new IndexedDictionary<string,int>();
col.Add("1", 1);
col.Add("2", 2);
col.AddAt(0, "0", 0); //add by index
col.Add("3", 3);
col.RemoveAt(2); //remove by index
string strList = string.Empty;
for (int i = 0; i < col.Count; i++)
{
strList += col[i]; //get by index
}
Assert.AreEqual(strList, "013");
}
As you can see, you can access the dictionary now by an index (aswell as your key). This is accomplished simply by inheriting from the Dictionary class and holding a List inside the class too :
    public class IndexedDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
protected List<TKey> m_col = new List<TKey>();

I have also added some extra features which can be toggled :

  • Replace Duplicate Keys - obviously you cannot have multiple entries in a dictionary with the same key, so what this does instead is replaces a value with the same key when adding to the collection
  • Throw Error On Invalid Remove - if disabled, when you try to remove an item that does not exist in the collection it just does nothing and does not throw an exception as it normally does

Immediately after this new collection came the NamedIndexedDictionary. This is a sub class of the IndexedDictionary class that takes a string as the key.

public class NamedIndexedDictionary<TValue> : IndexedDictionary<string, TValue>

The string key can also be set to be case insensitive or not, so calling col["abc"] is the same as col["ABC"].

Both have been very useful to me in the past and I hope someone else will find them useful too. Send me any other helpful collections you have created and I will add them too. 

Download here : collections.zip

 

Posted: Dec 13 2007, 12:30 PM by bradvin | with 4 comment(s)
Filed under: , , ,
Creating Extension Methods and Testing Them in VS2008

I wrote a beginners article on how to create your own extension methods in .NET 3.5 and then how to go about testing them using new built-in features in VS2008. Please go read the article at the Code Project : http://www.codeproject.com/useritems/ExtensionUnitTests.asp.

If you are interested in extension methods (like I am obviously) and plan on writing your own, I think the article is a good starting point. I give suggestions on when to use them and when I think it is not appropriate. I also talk about some unexpected behaviors that occur when using extension methods on variables that are null. It then walks you through a step by step guide on how to write unit tests for the extension methods using the new unit testing features of VS2008 pro.

Please check it out and comment on how I could improve it and please remember to rate the article.

Posted: Nov 04 2007, 09:13 PM by bradvin | with no comments
Filed under: , , ,
ÜberUtils - Part 3 : Strings

ÜberUtils Series posts so far :

So every developer has (or should have) a utilities class for strings. It seems the built-in string class never has enough (well for me in any case). So I hereby introduce my string utils class. It actually comprises of 3 files which are :

  1. Strings.cs (the actual string utils)
  2. SafeConvert.cs (a class for doing common conversions)
  3. Extensions/Strings.cs (extension methods using the string utils)

Here is the class diagram of the Strings class :

 

As you can see it has a nested class Regex which is also static. More on this later. Lets cover the string utility methods first (in 'logical' order):

  • IsEmpty - returns true if the object passed in is either null or has a length of zero (exactly like string.isNullOrEmpty but can take an object as input)
  • IsNumeric - returns true if we are dealing with a numeric value. Uses the regular expression : @"^\-?\(?([0-9]{0,3}(\,?[0-9]{3})*(\.?[0-9]*))\)?$". This matches a positive or negative value with any precision and scale (whole number or decimal). It also allows for left-padded zeros, commas as group separators or parenthesis to indicate negative number
  • IsEmail - returns true if an email. Uses the regular expression : @"([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)"
  • Trim - exactly like "abc".Trim() but adds checking for nulls
  • CutWhitespace - cuts all whitespace from a string aswell as trims it
    • eg. Strings.CutWhitespace(" 12  34   5 6  7   ") == "12 34 5 6 7"
  • CutEnd - chops the end n chars off the end of a string
    • eg. Strings.CutEnd("1234567890", 3) == "1234567"
  • CutStart - chops the first n chars off the beginning of a string
    • eg. Strings.CutStart("1234567890", 3) == "4567890"
  • Start - returns the first n chars of a string
    • eg. Strings.Start("1234567890", 3) == "123"
  • End - returns the last n chars of a string
    • eg. Strings.End("1234567890", 3) == "890"
  • GetOccurences - returns an array of strings that are found within another string based on a regular expression
    • eg. Strings.GetOccurences("say day bay toy", "[sdbt]ay") == new string[] {"say" , "day" , "bay"}
    • eg. Strings.GetOccurences("123asdasd 1sk 555 sdkfjsdfkl999", "\\d+") == new string [] {"123" , "1" , "555" , "999"}
  • OccurenceCount - returns the count of strings found within another string based on a regular expression
    • eg. Strings.OccurenceCount("the cat sat on the mat", "at") == 3
    • eg. Strings.OccurenceCount("abcabc", "a") == 2
  • Combine - combines a string array by a delimeter (or not) (DEPRICATED - read update and comments)
    • eg. Strings.Combine(Strings.GetOccurences("123asdasd 1sk 555 sdkfjsdfkl999", "\\d+"), ",") == "123,1,555,999"
    • eg. Strings.Combine(new string[] { "a", "b", "c", "d" }, ";") == "a;b;c;d"
  • ToPaddedNumber - returns a zero padded number (DEPRICATED - read update and comments)
    • eg. Strings.ToPaddedNumber("123", 5) == "00123"
  • XOR - performs a binary XOR operation on each char in the input string based on a key. Very simple form of encryption where XOR(XOR(input)) == input
    • eg. Strings.XOR(Strings.XOR("test", "key"), "key") == "test"
  • ToTitleCase - returns the title case of a string
    • eg. Strings.ToTitleCase("this is a title") == "This Is A Title"
  • ToFriendlyName - returns what I call a "friendly" version of a string. I use this mainly for converting a database field name into a user friendly name
    • eg. Strings.ToFriendlyName("IAmNotFriendly") == "I Am Not Friendly"
    • eg. Strings.ToFriendlyName("SomePrimaryKeyId") == "Some Primary Key"

Now onto the Regex class. The static Regex class just wraps regular expression functionality and contains a few commonly used expressions as constants. Here is the run down :

  •  IsExactMatch - returns true if a string is an exact match for a pattern
    • eg. Strings.Regex.IsExactMatch("test@google.com", Strings.Regex.REGEX_EMAIL) == true
  • Contains - returns true if a string contains a pattern
    • eg. Strings.Regex.Contains("here is my email : test@google.com", Strings.Regex.REGEX_EMAIL) == true
  • Replace - returns a string with a pattern replaced by another string
    • eg. Strings.Regex.Replace("1 23 a 456", @"\d+", "!") == "! ! a !"
  • GetMatch - returns the first match of pattern within a string
    • eg. Strings.Regex.GetMatch("Subject: Test Subject\r\n", @"Subject\s*\:\s*(?<SubjectReturn>.*)\r\n", "SubjectReturn") == "Text Subject"

Now onto the SafeConvert class. It contains the following methods :

  • ToBoolean - returns a boolean value from an object
  • ToInt - returns an integer value from an object
  • ToDecimal - returns a decimal value from an object
  • ToDouble - returns a double value from an object
  • ToHexString - returns a hexidecimal string representation of a byte array. This is used from Extensions\ByteArray.cs
  • ToStream - returns a System.IO.MemoryStream from a string

So thats version 1 of the strings utilities. I say version 1 because I will no doubt add to this over the next couple of posts.

Oh yes, and again we have a whole bunch of new extension methods :

  • Start
  • End
  • CutStart
  • CutEnd
  • OccurenceCount
  • GetOccurences
  • ReplaceAll - similar to Replace, but uses a regular expression to do the replacement
  • Split - similar to Split(char c) but takes a string pattern to split using regular expressions
  • Combine (DEPRICATED - read update and comments)
  • Join - an extension method for string arrays wrapping the string.Join method

Now I know some people might argue that this is extension method abuse, but look at how much more power my strings have :

 

... and anything that helps me code quicker and smarter is not abuse in my book - its smart coding!

Download the source code and unit tests here

UPDATE - thanks to Dan's comments we found a bug in the email regular expression whereby it would not allow the domain ".museum" so I changed the regex to
@"([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,8}|[0-9]{1,8})(\]?)" (changes in bold)
Please note that email validation seems to be a touchy point for many developers as can be seen over at haaked.com . I would suggest not to use ANY email validation like this for restricting comments or purchases online, as you would be limiting your site's reach. Source code and unit tests have been updated.

UPDATE - thanks to Scott Hanselman for pointing out that ToPaddedNumber is redundant as the string class has a PadLeft (as well a PadRight) method - DOH! Source code and unit tests have been updated.

UPDATE - thanks to Don and John for pointing out the fact that my Combine method is redundant as the string.Join method does the exact same thing. - oops ;)
I then renamed my extension method Combine to Join and changed it to wrap the string.Join functionality. Again Source and tests have been updated.

NOTE - I renamed the static extension classes so that you could include both the Utils and Utils.Extensions namespaces without getting the build error : 'Strings' is an ambiguous reference between 'Utils.Strings' and 'Utils.Extensions.Strings'. Please get the latest source.


Thanks for all comments and feedback and please keep it coming. Collaboration and a LOT of testing is the only way to produce robust,useful code!

Hello Silverlight - Start Your Silverlight Journey Today!

I think today is the start for great things to come. And why? Because I had my first article published today, thats why! You can find it over at Dotnetslackers : http://dotnetslackers.com/articles/silverlight/HelloSilverlightStartYourSilverlightJourneyToday.aspx. It basically is the walk through I was looking for when I was curious about Silverlight, but could never find.

It discusses what you need to start developing in Silverlight, what makes up your average Silverlight project, and walks you through a simple example.

If you found my article useful, I also found another good Silverlight article today at the codeproject :  http://www.codeproject.com/silverlight/BeginningSilverlightPart1.asp

happy coding 

Update : 
Ever wondered how you can render HTML in your silverlight controls? Delay has written an HTMLTextBlock control that does just that. Obviously it does not have a fully fledged HTML rendering engine behind the scenes, but it supports the more common tags. Check it out.
 

Posted: Oct 16 2007, 07:25 PM by bradvin | with 2 comment(s)
Filed under: ,
ÜberUtils - Part 2 : Cryptography (continued)

ÜberUtils Series posts so far :

I am now going to finish off the cryptography part of my ÜberUtils project. If you don't know about it, check it out. In the first article I released the hashing class. This post I am going to run through the Encryption class. The Encryption class is pretty straight forward and basically wraps the System.Security.Cryptography symmetric encryption classes. Here are the types of encryption available (this is exposed as an enum):

public enum EncryptionTypes
{
DES,
RC2,
Rijndael,
TripleDES
}

A simple example demostrates the ease of use :

Encryption target = new Encryption();
string inputText = "Thi$ is @ str!&n to tEst encrypti0n!";
string output = target.Encrypt(inputText);

You can also call a static method to make it even easier :

string strCiperText = Encryption.EncryptText("hello world!");

You can also as easily change the encryption algorithm, the password key or the salt. I am not going to write an article explaining how encryption works and what the different components are. If you are interested, check out this nice article I found on the subject.

You guessed it, it is as simple to decrypt too :

string strOriginal = Encryption.DecryptText(strEncryptedText);

Here is the class diagram :

 

Like the hashing class, the encryption class also uses some extension methods. Please note : extension methods should only be used when extending functionality of an existing class in a reusable fashion. Don't create an extension method just because you can, create one when you see yourself using the method over and over again in many places. This is my reasoning behind creating these extension methods. I feel they will be reused again in the future and it makes my life as a developer easier. Here they are :

        public static string ToBase64String(this byte[] data)
{
return Convert.ToBase64String(data);
}

public static string ToUTF8String(this byte[] data)
{
return new UTF8Encoding().GetString(data);
}

public static byte[] ToByteArrayUTF8(this string str)
{
return new UTF8Encoding().GetBytes(str);
}

public static byte[] ToByteArrayBase64(this string str)
{
return Convert.FromBase64String(str);
}

 So thats the encryption class. Included in the source, we again add unit tests for encryption. Download the source here. Enjoy.

Posted: Oct 07 2007, 09:35 PM by bradvin | with 1 comment(s)
Filed under: , , ,
.NET framework source to be released

Well if you havent heard the good news already, then go read about it here at ScuttGu's Blog : Releasing the Source Code for the .NET Framework Libraries . As the name of the article implies, Microsoft is planning to release a standalone install that contains the .NET Framework source libraries. It will also include integrated debugging support within VS2008.

WOW!!!!

If you develop using .NET there must have been a time when you needed to delve into the framework code. Thank goodness for the Reflector tool written by Lutz Roeder. It has been the only way to see what the framework is actually doing, by decompiling the framework code:

Or, if you're like me and you want to view the code within VS, then you can download Denis Bauer's Reflector add-in Reflector.FileDisassembler . This allows you to dump a whole framework DLL or single class to a file so you can play :

 

OK, back to the topic - .NET FRAMEWORK SOURCE CODE! Go check out ScottGu's post which shows him stepping through the code within VS2008.

Another amazing feature he talks about is the ability to step into the source even if you haven't downloaded the source. Just step into a framework method and VS2008 will automatically download the appropriate source file from Microsoft. All built in! Pretty awesome.

Posted: Oct 04 2007, 08:17 AM by bradvin | with no comments
Filed under: ,
ÜberUtils - Part 1 : Cryptography

ÜberUtils Series posts so far :

If you read my previous post, you would know that it is my time to give back to the community. So where should you start when creating the ULTIMATE REUSABLE UTILS CLASS LIBRARY? (from now on called ÜberUtils)? Good question, and thats the reason why its taken me a while to start this series, cause I had to start it at exactly the right point. I thought back to the first utils class I wrote back in 2003 in .NET 1.1. It was a cryptography class that wrapped existing functionality that exists in the system.security.cryptography namespace.
So I figure thats a good place to start again. But first I wanna note a few things :

  • This code will be written for .NET 3.5 framework (this is to take advantage of the newer features)
  • I will be using VS2008 beta 2 which, which at this point in time (Oct 2007), you can download for free. (I want to utilise some of the new features available e.g. Unit Testing)
  • The namespaces I use will be as simple and generic as possible, meaning that it will be simple to incorporate into any number of companies / projects / systems / architectures etc. I hate having weird long namespaces when I use other developers libraries. I would prefer a namespace like "Utils" rather than "BradVin.Core.Utils". Wouldnt you agree? (I was also thinking about using "System.Utils")
  • The project files and code can be downloaded from codeplex at the ÜberUtils project page.
  • Code license : public domain. This means that all the code in this series is absolutely free, and by definition : the code may be freely reproduced, distributed, transmitted, used, modified, built upon, or otherwise exploited by anyone for any purpose, commercial or non-commercial, and in any way, including by methods that have not yet been invented or conceived. So rip it to pieces, pull out what you like - I DONT CARE! My only stipulation is this : IF YOU ADD SOMETHING USEFUL PLEASE SHARE IT WITH US!!!!! (you can do this by commenting on this blog or on discussing it at code plex)


I'm gonna start with a Hashing class which is extremely simple to use. This class makes it easy to hash a byte array or a string e.g.

string strHash = Hashing.Hash("test");

You can also use different hashing algorithms e.g.

string strHash = Hashing.Hash("test", Hashing.HashingTypes.SHA512);

Internally, the different hashing algorithm classes used are : SHA1CryptoServiceProvider, SHA256Managed, SHA384Managed, SHA512Managed and the default MD5CryptoServiceProvider.

Very simple isn't it?. Here is the class diagram :

If you have a look at the code, you would have noticed some strange new methods on both string and byte array. This is using a new .NET 3.5 feature called extension methods. If you've not heard of it yet, the best explanation is by ScottGu here. Infinities Loop also talks about it here and ScottGu again here . So let's see how I implement my extension methods. Check this code out :

private string ComputeHash(string inputText)
{
//convert output byte array to a string
return ComputeHash(inputText.ToByteArray()).ToHexString().ToUpper();
}
You'll see a string has a new method called ToByteArray. Also note that the ComputeHash method returns a byte array, and this means that a byte array now has a new method too : ToHexString. Here is the code for these 2 extension methods :
namespace Utils.Extensions
{
public static class ByteArrays
{
public static string ToHexString(this byte[] data)
{
StringBuilder sb = new StringBuilder(data.Length * 2);
foreach (byte b in data)
{
sb.AppendFormat("{0:x2}", b);
}
return sb.ToString();
}
public static byte[] ToByteArray(this string str)
{
return ASCIIEncoding.ASCII.GetBytes(str);
}
}
}

Now all I had to do to use these methods was add a using at the top of my class :

using Utils.Extensions;

But I didn't want to just stop here. I've had a scenario in the past when I needed to compute a MD5 hash of a string. I also found this blog post on the subject and decided I can now use extension methods to add this functionality to every string. So heres the code :

namespace Utils.Extensions
{
public static class Cryptography
{
public static string GetMD5HashCode(this string str)
{
return Utils.Cryptography.Hashing.Hash(str);
}
}
}

So now I can use it on any string e.g.

string actual = "test".GetMD5HashCode();
So thats the Hashing class. This post ended up a lot longer than I expected, so in my next post Im going to add an encryption class. Please check out the code (Download here or get it from my CodePlex project page). Included in the source are unit tests that check the hashing is doing its job correctly. (I used the  System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile method to make sure the tests are valid and double checked the results against other non-C# hashing algorithms e.g. http://pajhome.org.uk/crypt/md5/ )

 

Posted: Oct 02 2007, 07:37 PM by bradvin | with 4 comment(s)
Filed under: , , ,
More Posts Next page »