I Love ReSharper

Wed 24 January, 2007

This was going to be a post about the different VS 2005 plugins that I use, and some other useful programs for my everyday (most of them Open Source, by the way); but while I was writing it turned itself little by little into a love letter to Resharper. I can only say good things about this productivity plugin.

This are only some of its features:

  • Automatic warning and error checking while you write code; similar to the automatic ortographic revision from MS Office but quite more smart.
  • Better Intellisense. By far, my preferred feature and the one you tend to note first. If you want it to, ReSharper can completely replace VS2005’s Intellisense with its own, faster and more complete. You have the possibility of setting the same font for Intellisense and source code, you can replace Intellisense icons with ReSharper’s set, and add a pop-up with the complete method signature.
  • Code auto-completing, you write the first three characters of virtually anything and ReSharper is already displaying a list of alternatives. If what you’re writing is a method call, pressing TAB inserts the complete method name along with the () and the ;.
  • Templates.
  • Refactoring: you can extract properties from variables, or methods from code: you select a code block, select to extract it as a method, give to it a name and some options, among them are a list of possible parameters that ReSharper automatically detects and voilá!: method created.
  • Advanced search for method use.
  • Type hierarchy view
  • Unit test running with NUnit or csUnit from the IDE.
  • Context actions. In some sections of your code you can see a small floating window with a lightbulb icon. ReSharper wants to help: opening that window with your mouse (or even better, pressing ALT+Enter) ReSharper will show you a list of all the possible actions it can make depending on the code context: check for null values, reorder if sentences, refactor string concatenatios with an StringBuilder, delete unused references, and so on.
  • Automatic detection of unused variables, methods or references. If you declare a variable at the beginning of your code that you later don’t use ReSharper will remark it using a grey color (trust it, it’s quite more noticeable than it sounds), and with that you know that it’s not being used anywhere. ReSharper uses the same color to remark redundant and useless casts or uses.
  • Quick jump to a method or variable definition: using CTRL+Click on the method or variable name you can jump directly into the method or variable definition, if the code is avaliable.
And these are only the tool’s highlights, the tasks I do daily with it. Definitely, it’s the best buy I’ve ever made for a Visual Studio add-in; mostly because I used this last Christmas offer and got it for $99 instead of its normal price tag of $249, which is quite unexpensive if your firm buys it for you: not my case. I bought ReSharper for my exclusive use.

JetBrains development team make Visual Studio 2005 a better yet IDE with this product. The ongoing rumor that they are developing their own version of a .NET IDE, building of IDEA’s true and tested success is, sadly, outdated (look the comments), because it seems the project is abandoned. A real pity.

Random Surname Generator

Fri 12 January, 2007

Aswering the challenge raised by Jon Galloway on his must-read blog, here’s is my solution to generate some random and fake surnames. Code has plenty of comments. Feel free, of course, to comment questions, criticism or even better, a cool refactor! =)

class Program
{
    
//An arraylist to contain ALL possible digrams (see below)
    
private static ArrayList AllDigrams = new ArrayList();
    
//An array list to contain ALL possible letters (see below)
    
private static ArrayList AllLetters = new ArrayList();
    
//A random seed for the main random generator
    
private static Random rSeed = new Random()
    
//A multidimensional array specifying the random "bones" of a surname. 
    // "A" is a vowel letter, "B" is a consonant letter
    //"AB is a vowel + consonant digram, and so on…
    //These combinations are made by hand, you could add or delete 
    //to fine-tune the kind of surnames you get
    
private static string[,] SurnameRandomTypes = new string[106]
            {
                {
"A",  "BA""B",  "A",  "BA"""},
                {
"B",  "A",  "BA""",   "",   ""},
                {
"A",  "BA""A",  "B",  "A",  "BB"},
                {
"A",  "B",  "B",  "A",  "BA"""},
                {
"A",  "BB""AB""A",  "",   ""},
                {
"BA""B""B",  "A",  "B",  "A"},
                {
"B",  "AB""A",  "BB""A",  "AB"},
                {
"B",  "AB""B",  "A",  "BB""AB"},
                {
"A",  "BB""A",  "B",  "AB"""},
                {
"AB""AA""B",  "A",  "B",  "A"},
            }
;

    /// <summary>
    /// 
    /// </summary>
    
static void Main()
    {
        
bool exit = false;
        
//We load ALL the possible digrams in memory, 
        //in the AllDigrams arraylist
        
LoadDigrams();
        
//Same for the possible letters, 
        //in the AllLetters arraylist
        
LoadLetters();
        do
        
{
            
for (int 0i <20i++)
            {
                Console.WriteLine(
"Random Surname:    {0}"
                    GenerateRandomSurname())
;
            
}
            Console.WriteLine(
"Press ENTER to generate another batch, X to quit");
            string 
sInput Console.ReadLine();
            if 
(sInput.ToString() == "x" || sInput.ToString() == "X")
                exit 
= true;
        
while (exit == false);
        
Console.ReadLine();
    
}

    /// <summary>
    /// This method generates a random surname and returns it.
    /// </summary>
    /// <returns></returns>
    
static string GenerateRandomSurname()
    {
        
string sRet "";
        
Random r;
        
//New random seed for the randomizer
        
= new Random(rSeed.Next(11000));
        
//and we get a random index for the 
        //SurnameRandomTypes multidimensional array
        
int rndIndex r.Next(09);
        
//Then we read the different "columns" of 
        //that "row" and decide if we need a vowel, 
        //a consonant, a vowel-consonant digram, 
        //or whatever
        
for (int 0i < 6i++)
        {
            
switch(SurnameRandomTypes[rndIndex, i])
            {
                
case "AA":
                    sRet +
getDigram(DigramType.TwoVowels);
                    break;
                case 
"AB":
                    sRet +
getDigram(DigramType.VowelAndConsonant);
                    break;
                case 
"BA":
                    sRet +
getDigram(DigramType.ConsonantAndVowel);
                    break;
                case 
"BB":
                    sRet +
getDigram(DigramType.TwoConsonants);
                    break;
                case 
"A":
                    sRet +
getLetter(LetterType.Vowel);
                    break;
                case 
"B":
                    sRet +
getLetter(LetterType.Consonant);
                    break;
                default
:
                    
break;
            
}
        }
        
//And we return the generated surname 
        //making the first letter upper case
        
return sRet.Substring(0,1).ToUpper() + 
            sRet.Substring(
1);
    
}

    #region Letters and Digrams
    
    
#region Structs and Enums
    
/// <summary>
    /// With this enum we’ll know what 
    /// the digram is made of
    /// </summary>
    
private enum DigramType
    {
        TwoVowels,
        VowelAndConsonant,
        ConsonantAndVowel,
        TwoConsonants
    }

    /// <summary>
    /// With this enum we’ll know is a letter
    /// is a vowel or not
    /// </summary>
    
private enum LetterType
    {
        Vowel,
        Consonant
    }

    /// <summary>
    /// A digram. With the actual digram ("th", for example)
    /// we store the range of frequency it appears on the 
    /// english language and its type
    /// </summary>
    
private struct Digram 
    {
        
public int iniRange;        //The beginning of its range
        
public int endRange;        //The end of its range
        
public string Value;        //Its actual content
        
public DigramType Type;     //Its type
    
}

    /// <summary>
    /// A letter. With the actual letter ("a", for example)
    /// we store the range of frequency it appears on the 
    /// english language and its type
    /// </summary>
    
private struct Letter
    {
        
public int iniRange;        //The beginning of its range
        
public int endRange;        //The end of its range
        
public string Value;        //Its actual content
        
public LetterType Type;     //Its type
    
}
    
#endregion

    /// <summary>
    /// This method returns the value 
    /// of a random digram of the specified type
    /// </summary>
    /// <param name="type">The type of digram the caller needs</param>
    /// <returns>The digram VALUE ("th", for example)</returns>
    
static string getDigram(DigramType type)
    {
        Random r 
= new Random(rSeed.Next(01000));
        
Digram nuDigram = new Digram();
        do
        
{
            
//We get a random number inside the TOTAL 
            //range of ALL the digrams
            
int freq r.Next(05548)//See LoadDigrams()
            
foreach (Digram digram in AllDigrams)
            {
                
//And if the random number is inside the 
                //current digram frequency range, 
                //we’ve got a winner!
                
if (freq >digram.iniRange && 
                    freq <
digram.endRange) 
                        nuDigram 
digram;
            
}
            
//But only if it’s of the type we need
        
while (nuDigram.Type !type);
        return 
nuDigram.Value;
    
}

    /// <summary>
    /// Gets a single letter VALUE of the specified type
    /// </summary>
    /// <param name="type">the type of Letter we need</param>
    /// <returns>the Letter VALUE</returns>
    
static string getLetter(LetterType type) 
    {
        Random r 
= new Random(rSeed.Next(01000));
        
Letter nuLetter = new Letter();
        do
        
{
            
//We get a random number inside the TOTAL 
            //range of ALL the letters
            
int freq r.Next(010025)//See LoadLetters()
            
foreach (Letter letter in AllLetters)
            {
                
//And if the random number 
                //is inside the current digram 
                //frequency range, we’ve got a winner!
                
if (freq >letter.iniRange && 
                    freq <
letter.endRange)
                        nuLetter 
letter;
            
}
            
//But only if it’s of the type we need  
        
while (nuLetter.Type !type);
        return 
nuLetter.Value;
    
}

    /// <summary>
    /// Simple boolean check to see if a given single letter is
    /// a vowel or a consonant
    /// </summary>
    /// <param name="checkLetter">The letter to check</param>
    /// <returns>True if vowel, false if else</returns>
    
static bool IsAVowel(string checkLetter)
    {
        
switch(checkLetter.ToLower())
        {
            
case "a":
            
case "e":
            
case "i":
            
case "o":
            
case "u":
                
return true;
            default
:
                
return false;
        
}
    }

    /// <summary>
    /// Thanks to the University of Bristol Department of Computer Science
    /// (and Google) I’ve managed to get a list of all the letters in the 
    /// english alphabet and their frequency of use in the English language
    /// Since Random deals with integers, I’ve multiplied all these values * 100
    /// The sum of all of them should be 10,000, which when calculating the 
    /// range of random numbers to draw should be 10,000 + the number of possible 
    /// letters; because the first letter range is from 0 to 1231, and the next 
    /// letter range begins en 1232 (prior end range +1)
    /// In the case of single letters this is correct, and the ranges are from 
    /// 0 to 10,025. 
    /// 
    /// But, I don’t know WHY, in the case of digrams the range goes 
    /// from 0 to 5,548. The frequency percentage of all the avaliable digrams,
    /// each of them multiplied by 100 and all of them added is NOT 10,000 as it 
    /// should be. Anyone knows why?
    /// </summary>
    
static void LoadLetters()
    {
        
// http://www.cs.bris.ac.uk/Teaching/Resources/COMS30124/Labs/freq.html
        // Percentage Frequency of Single Letters
        //
        //  E 12.31      L 4.03     B 1.62
        //  T  9.59      D 3.65     G 1.61
        //  A  8.05      C 3.20     V 0.93
        //  O  7.94      U 3.10     K 0.52
        //  N  7.19      P 2.29     Q 0.20
        //  I  7.18      F 2.28     X 0.20
        //  S  6.59      M 2.25     J 0.10
        //  R  6.03      W 2.03     Z 0.09
        //  H  5.14      Y 1.88      

        string[] _letterValue 
        
{
            
"e""l""b""t""d""g""a""c""v",
            
"o""u""k""n""p""q""i""f""x",
            
"s""m""j""r""w""z""h""y"
        
};
        int
[] _letterFreq 
        
{
            
123140316295936516180532093
            
794310527192292071822820,
            
659225106032039514188
        
};
        int 
lastEndRange 0;
        
//Examining those hard-coded arrays we fill 
        //the AllLetters collection of letter objects, 
        //assigning each one its range, value and LetterType
        
for (int 0i < _letterFreq.Lengthi++)
        {
            Letter nuLetter 
= new Letter();
            
nuLetter.iniRange lastEndRange;
            
nuLetter.endRange lastEndRange + _letterFreq[i];
            
lastEndRange nuLetter.endRange + 1;
            
nuLetter.Value _letterValue[i];
            
nuLetter.Type IsAVowel(_letterValue[i]) ? 
                LetterType.Vowel : LetterType.Consonant
;
            
AllLetters.Add(nuLetter);
        
}
    }
     
    
/// <summary>
    /// See LoadLetters comment
    /// </summary>
    
static void LoadDigrams()
    {
        
// http://www.cs.bris.ac.uk/Teaching/Resources/COMS30124/Labs/freq.html
        //
        //  TH  3.15   TO  1.11   SA  0.75   MA  0.56 
        //  HE  2.51   NT  1.10   HI  0.72   TA  0.56
        //  AN  1.72   ED  1.07   LE  0.72   CE  0.55
        //  IN  1.69   IS  1.06   SO  0.71   IC  0.55
        //  ER  1.54   AR  1.01   AS  0.67   LL  0.55
        //  RE  1.48   OU  0.96   NO  0.65   NA  0.54
        //  ES  1.45   TE  0.94   NE  0.64   RO  0.54
        //  ON  1.45   OF  0.94   EC  0.64   OT  0.53
        //  EA  1.31   IT  0.88   IO  0.63   TT  0.53
        //  TI  1.28   HA  0.84   RT  0.63   VE  0.53
        //  AT  1.24   SE  0.84   CO  0.59   NS  0.51
        //  ST  1.21   ET  0.80   BE  0.58   UR  0.49
        //  EN  1.20   AL  0.77   DI  0.57   ME  0.48
        //  ND  1.18   RI  0.77   LI  0.57   WH  0.48
        //  OR  1.13   NG  0.75   RA  0.57   LY  0.47 

        string[] _digramValue 
        
{   
            
"th""to""sa""ma""he""nt""hi""ta",
            
"an""ed""le""ce""in""is""so""ic",
            
"er""ar""as""ll""re""ou""no""na",
            
"es""te""ne""ro""on""of""ec""ot",
            
"ea""it""io""tt""ti""ha""rt""ve",
            
"at""se""co""ns""st""et""be""ur",
            
"en""al""di""me""nd""ri""li""wh",
            
"or""ng""ra""ly"
        
};
        int
[] _digramFreq 
        
{
            
31511175562511107256,
            
17210772551691067155
            
1541016755148966554
            
145946454145946453
            
131886353128846353
            
124845951121805849
            
120775748118775748
            
113755747
        
};
        int 
lastEndRange 0;
        
//Examining those hard-coded arrays 
        //we fill the AllDigrams collection
        //of Digram objects, assigning each 
        //one its range, value and DigramType
        
for (int 0i < _digramFreq.Lengthi++)
        {
            Digram nuDigram 
= new Digram();
            
nuDigram.iniRange lastEndRange;
            
nuDigram.endRange lastEndRange + 
                _digramFreq[i]
;
            
lastEndRange nuDigram.endRange + 1;
            
nuDigram.Value _digramValue[i];
            if 
(IsAVowel(_digramValue[i].Substring(01)) && 
                IsAVowel(_digramValue[i].Substring(
11)))
                    nuDigram.Type 
DigramType.TwoVowels;
            else if 
(!IsAVowel(_digramValue[i].Substring(01)) && 
                IsAVowel(_digramValue[i].Substring(
11)))
                    nuDigram.Type 
DigramType.ConsonantAndVowel;
            else if 
(IsAVowel(_digramValue[i].Substring(01)) && 
                !IsAVowel(_digramValue[i].Substring(
11)))
                    nuDigram.Type 
DigramType.VowelAndConsonant;
            else 
nuDigram.Type DigramType.TwoConsonants;
            
AllDigrams.Add(nuDigram);
        
}
    }
#endregion
}


Colorized by: CarlosAg.CodeColorizer

And here’s an example of the surnames it makes:

  • Andaso
  • Estecon
  • Ovloso
  • Eleasawh
  • Athene
  • Tertore
  • Setolyiis
  • Face
  • Lirsuco
  • Rofsender

kick it on DotNetKicks.com

Compressing and uncompressing ZIP files

Thu 4 January, 2007

For compressing and uncompresing files in ZIP format we have a very valuable tool at our disposal: the SharpZipLib library, made by IC#Code, the same developer group that brought us the awesome Open Source IDE for C# SharpDevelop, which I’ve mentioned before.

First thing we’ve got to do, then, is to download said library to our system. Once donde that, we must include it as a reference on the project we’re using it; and if we believe we’re going to use it somewhat frequently, the comfy option is to install it to the GAC. To do so, let’s open the Visual Studio 2005 Command prompt, located under Tools in the Visual Studio 2005 start menu group. When clicking on it, it opens a new console window and inside it we should go to the folder where we’ve downloaded ICSharpCode.SharpZipLib.dll (for easiness, I have it on a folder of its own at C:\Program Files) and once there we enter the following command:

gacutil /i ICSharpCode.SharpZipLib.dll

Thus we add the library to .NET’s global assembly cache.

So, either if we add the library to the GAC or not, we have to reference it in our project. For it, you know, once our project is created (be it a Console, Windows Forms or Web Forms) whe choose the Project, Add Reference menu item, and we choose the library on the dialog window that opens: on the .NET tab if the library is included in the GAC or we can search it with the tab Browse. Once the reference is included, we add the line


using ICSharpCode.SharpZipLib.Zip;

in our code and we are set to go.
One last disclaimer before gettint to it: to simplify this article I’ve decided for the code examples included to work in the Console and I’ve taken for granted some things like paths and file names. If you want this code to work don’t simply copy and paste it, you’ve got to adapt it to your system and your files.

Listing File Content

Let’s see first how can we list the contents of a ZIP file. for it, we simply declare an object called zip from the class ZipFile. See how SharpZipLib makes a ZipFile composed of ZipEntry objects, not of files or lines or any other thing. The ZipEntry class is going to be any of the entities that can be contained on a ZIP file, files or directories. Once knowing this, we can work our way through the entries on the ZIP file with a foreach loop and we simply show the name of the entry on the Console:

private static void ListZipContent(string sFile) 
{
    ZipFile zip 
= new ZipFile(File.OpenRead(sFile));
    foreach
(ZipEntry entry in zip) 
    {
        Console.WriteLine(entry.Name)
;
    
}

Colorized by: CarlosAg.CodeColorizer

Uncompressing Files

The plot thickens. To uncompress a ZIP file we open it as a ZipInputStream object, we navigate through its entries with the GetNextEntry() method and we save the binary content of each entry on a byte array. Said array will feed a StreamWriter object, which we’ll use to create the new file, which will be an exact uncompressed copy of the binary data stored on the ZIP file’s entry.

private static void UncompressZip(string sFile)
{
    ZipInputStream zipIn 
= new ZipInputStream(File.OpenRead(sFile));
    
ZipEntry entry;
    while 
((entry zipIn.GetNextEntry()) != null)
    {
        FileStream streamWriter 
File.Create(@"C:\Temp\" + entry.Name);
        long 
size entry.Size;
        byte
[] data = new byte[size];
        while 
(true)
        {
            size 
zipIn.Read(data, 0, data.Length);
            if 
(size > 0) streamWriter.Write(data, 0, (int) size);
            else break;
        
}
        streamWriter.Close()
;
    
}
    Console.WriteLine(
"Done!!");

Colorized by: CarlosAg.CodeColorizer

Compressing Files

For compressing files wer’ll use an object similar to the previous one but of the opposite purpose, ZipOutputStream. Wanting to keep this example simple, I’ve supposed we want to compress every existing file on the path passed as parameter sPath. Thus we’ll read all the files sitting on that folder, we’ll create a new ZipEntry object, get some information about the file with FileInfo (we could also use some CRC checking, but I wanted to keep this simple) to assign that information to the new entry, and add the entry to the ZipOutputStream object with its PutNextEntry method. Pay attention, this would create an empty entry: to really enter the data contained in the file we need to read its binary data with a FileStream as in the previous example and then call the method Write from ZipOutputStream to save those binary data on the most recent entry of the output Zip stream. Once read and included all the files, we finish and close the ZipOutputStream object.

private static void CompressZip(string sPath) 
{
    ZipOutputStream zipOut  
= new ZipOutputStream(File.Create(@"C:\Temp\test.zip"));
    foreach
(string fName in Directory.GetFiles(sPath))
    {
        FileInfo fi  
= new FileInfo(fName);
        
ZipEntry entry = new ZipEntry(fi.Name);
        
FileStream sReader File.OpenRead(fName);
        byte
[] buff = new byte[Convert.ToInt32(sReader.Length)];
        
sReader.Read(buff, 0, (int) sReader.Length);
        
entry.DateTime fi.LastWriteTime;
        
entry.Size sReader.Length;
        
sReader.Close();
        
zipOut.PutNextEntry(entry);
        
zipOut.Write(buff, 0, buff.Length);
    
}
    zipOut.Finish()
;
    
zipOut.Close();
    
Console.WriteLine("Done!!");

Colorized by: CarlosAg.CodeColorizer

And this ends this little introduction to SharpZipLib. IC#Code keeps an superb forum on which you can find some more code examples and a lot of answered questions.

Update 5/01/2007.- Playing a little bit with the library and VS 2005’s Object Browser, I stumble upon a class called FastZip. Hmmmmm. Yeah, it’s what it seems: a wrapper class to ease the process of compressing and uncompressing files with SharpZipLib. For example, to uncompress a file it would be as simple as this:

ExtractZip method’s three parameters are strings; and they stand for the name of the ZIP file to uncompress, the destination path for the uncompressed files and a mask for the type of files we want to extract. Warning: you can’t use an DOS mask such as "*.txt", you must use a regular expression. If we want to uncompress every file existing on the path, we should simply leave this last argument as null or empty string "".

private static void FastZipUncompress(string sFile)
{
    FastZip fZip 
= new FastZip();
    
fZip.ExtractZip(sFile, @"C:\Temp""");

Colorized by: CarlosAg.CodeColorizer

And to compress:

private static void FastZipCompress()
{
    FastZip fZip 
= new FastZip();
    
fZip.CreateZip(@"C:\Temp\walls.zip"@"C:\Wallpapers"false".jpg$");
    
Console.WriteLine("Done!!");

Colorized by: CarlosAg.CodeColorizer

In this example the method CreateZip accepts four arguments: the name and path of the ZIP file to be created, the folder where the files we want to compress are, a boolean parameter for stating if we want to make a recursive compression (to include possible subfolders), and last an string parameter specifying the mask we want to use for choosing the files we want to compress, in this example every JPG file. The same rules in using masks applies.

kick it on DotNetKicks.com

XNA development

Fri 1 September, 2006

Although everyone and his brother has already posted this, I gotta try…

Microsoft has made avaliable the beta of XNA Game Studio Express, a set of free tools for programming games targeted to Windows and XBox. It includes the XNA Framework, which is no less than a collection of managed libraries geared towards a (much needed) simplification of using DirectX.

Oriented to students and amateur programmers (and actually reaching out to almost every programmer1) at the moment XNA Game Studio is only compatible with Visual C# 2005 Express.

Tehre’s already a site (that I know of) collecting all the download links for all the needed tools (and yeah, they’re all free); and some tutorials to boot: XNA Development.

I don’t know why, but I don’t think I’m going to get out much this weekend…


1.- C’mon, which one of you has not once dreamed about game programming? It’s the Top Gun of computer programming!

SubSonic

Wed 30 August, 2006

Wow. I’ve just hurt myself: I’ve just seen Rob Conery’s webcast about the little library he’s created for .NET 2.0 and myu jaw has opened so much all by itself, and not precisely yawning, that it has crashed against my desk.

As Conery himself says, he’s followed Ruby on Rails for creating SubSonic (previously known as Action Pack). SubSonic is a Zero Code DAL, or Data Access Layer. Just like in Ruby, complete with scaffolding.

Beware, SubSonic is not a .NET implementation of Ruby On Rails. That’s MonoRail. It’s not even an adaptation, because RoR is a complete framework with Modell-View-Controller , data access layer and all the works. SubSonic is just an amazing data access layer.

I haven’t got the time (yet) to play a little with it, but if you’d like to stop feeling envy while watching Rails webcasts or presentations, take a look at the Introduction to SubSonic webcast.

Beware you jaws.

Update.- I forgot to link this excellent introduction by Jon Galloway. I’m of the same opinion: Microsoft should hire Conery and include the ActionPack.dll as part of Atlas. Or for giving SubSonic MVC capabilities. Or doing something with this stuff.

SharpDevelop 2

Mon 31 July, 2006

Two very good news for this post. First of all is the recent update of our favorite Open Source IDE for C#, SharpDevelop, to Framework 2.0. Yeah I know, this isn’t strictly new, because it’s not recent (and how difficult is being in this day and age!): it was the last July the 17th when it made its official appereance the definitive version of SharpDevelop 2 2.0.

But now let’s talk about the second piece of news. After downloading it ( 4.2 Mbs) and install it (less than five minutes) I begin to try it. When creating a new try it out solution (another Hello, world!) I began to tremble: compatibility troubles on sight.

SharpDevelop 1 used combines, file swith a .cmbx extension, as its central type file: SharpDevelop combines were the equivalent to VS .NET solutions. So when you spotted on your hard drive a .cmbx file you knew it was SharpDevelop’s, and it was by default associated on Windows Explorer to that application. Files such as .cs or .resx you could decide wich IDE you want them associated to.

But now the combines are lost, being replaced with solutions, just as in Visual Studio. And their file extension is the same: .sln.

The pain. I was already picturing myself creating separate root folders for Vs and SharpDevelop projects, I was already picturing both IDEs fighting for the .sln association like two furious web browsers, I was already picturing myself corrupting forever a solution after opening it with the wrong IDE, and so on.

Hold your horses: after creating a nice, little and silly solution in VS .NET 2005, I’ve been able to open, modify and compile it without fuss in SharpDevelop 2. And viceversa, the same solution can be opened, modified and compiled by both IDEs with no troubles that I detected.

I wish I could do a much more serious testing, for example an ASP .NET application, but for now I can declare both IDEs totally compatible. And this is a very good news.

Web 2.0 and all that…

Mon 10 April, 2006

Via Mike Gunderloy (again… if you’re not suscribed already, don’t know what you’re waiting for) I’ve found two amazing libraries for ASP .NET.

First, BusyBoxDotNet, a server control which displays a wait message on the page stating that the server is performing a time-consuming operation and unabling the user to do nothing else but wait. The message is shown on a layer which moves automatically when scrolling, and allows to gray out and lock the back of the page (that is, all the rest of it) while the message is displayed and the task is (hopefully) being performed, to dissapear without a fuss at the end. You can try a demo right here. Really useful. Kudos.

And second, hot in the Web 2.01 hype, we find MagicAjax.NET. It allows us to enter AJAX techniques on standard out-of-.the.box ASP .NET server controls, which I find particularly useful for applying AJAX on an already developed or well underway ASP .NET application. It also has a demo avaliable.


1 And no, it’s not only to make rounded borders to visible layers. Neither putting an RSS feed on your pages, neither using words such as sinergy, community, collaborative or even tags. Even if you place a Beta label neatly by the side of your application’s title, you fall short. Besides all that, you have to make your web application more intuitive, more responsive, more agile. Less goings to the server for information, at least in appeareance. Yes, it has to be as similar as possible to an old desktop application, even imitating them completely. Live and see.

Writing and reading images on a Microsoft Access database

Mon 3 April, 2006

If just a few days ago we talked about how to store binary files on SQL Server, today’s Microsoft Access turn. The process is similar, though for this example I’ve chosen to use image files as the binary data we are going to store and retrieve from the database.

Let’s say we have a simple Access database, with a single table called Images, with the following layout:

Field Name Type
imgID int (Autonumeric Key)
FileName Text(255)
ImageData OLE Object

imgID will store a unique autonumeric key for identifying each record. FileName will have the name of the image file, and ImageData will store the image itself, as binary data.

The procedure to insert images on the Access database is as follows:

private void SaveData(string fileName)
{
    
try
    
{
        
//Creamos un nuevo FileStream a partir del fichero parámetro
        //
        //We create a new FileStream from the file specified as parameter
        
FileStream fs = new FileStream(fileName, FileMode.Open);
        
//Declaramos un array de bytes del tamaño del FileStream
        //
        //We declare a new byte array as big as the FileStream
        
Byte[] data = new byte[fs.Length];
        
//E introducimos los datos del FileStream en el array
        //
        //And we enter the data from the FileStream into the array
        
fs.Read(data, 0, Convert.ToInt32(fs.Length));
        
fs.Close();

        //Creamos un nuevo DataAdapter, DataSet, CommandBuilder and DataRow
        //para insertar el nuevo registro
        //
        //We create a new DataAdapter, DataSet, CommandBuilder and DataRow 
        //to perform the insertion of the new record
        
if(conn.State !ConnectionState.Open ) conn.Open();
        
OleDbDataAdapter adap 
             new 
OleDbDataAdapter("SELECT * FROM Images", conn);
        
adap.MissingSchemaAction MissingSchemaAction.AddWithKey;
        
OleDbCommandBuilder cmdBuilder = new OleDbCommandBuilder(adap);
        
adap.InsertCommand cmdBuilder.GetInsertCommand();
        
DataSet ds = new DataSet();
        
adap.Fill(ds);
        
DataRow dr ds.Tables[0].NewRow();
        
dr["FileName"fileName
        
dr["ImageData"data//The byte array
        
ds.Tables[0].Rows.Add(dr);
        
adap.Update(ds);
        
conn.Close();

        //Esta funcion refresca un ListBox para que se refleje 
        //el nuevo registro insertado
        //
        //This function refreshes a ListBox to show the newly inserted record
        
LoadData();
        
MessageBox.Show("Done!\nImage file: " + fileName + 
            " added to the database.");
    
}
    
catch (Exception e)
    {
        MessageBox.Show(e.Message)
;
    
}
}


Colorized by: CarlosAg.CodeColorizer

And the procedures to extract images from the database are like these:

/// <summary>
/// Coloca en el control Image la imagen recuperada de base de
/// datos por la función LoadImage, en base al índice único de 
/// la imagen en la base de datos.
/// 
/// Sets in the Image control an image recovered from the database
/// via the LoadImage function, thanks to the unique identifier 
/// of the image on the database.
/// </summary>
/// <param name="imgIndex">El identificador único de la imagen 
///en la base de datos</param>
/// <param name="imgIndex">The unique identifier of the image 
///on the database</param>
private void LoadImageControl(string imgIndex)
{
    
try
    
{
        
//Creamos un nuevo stream de memoria con el resultado 
        //de la función LoadImage, que devuelve un array de bytes
        //
        //We create a new MemoryStream with a byte array,
        //returned by the LoadImage function
        
MemoryStream mem = new MemoryStream(LoadImage(imgIndex));
        
//Y establecemos que la imagen del control picImage es una nueva imagen, 
        //a partir del stream en memoria mem
        //
        //And we set the picImage control’s image is a new image, 
        //built from the memory stream mem
        
picImage.Image Image.FromStream(mem);
    
}
    
catch (Exception e)
    {
        MessageBox.Show(e.Message)
;
    
}
}

/// <summary>
/// Carga la imagen de la base de datos en un array 
//de bytes y devuelve dicho array
/// 
/// Loads the image from the database to a byte array and returns that array
/// </summary>
/// <param name="imgIndex">El ID unico de la imagen</param>
/// <param name="imgIndex">The unique ID of the image</param>
/// <returns>Devuelve la imagen en un array de bytes</returns>
/// <returns>The image on a byte array</returns>
private byte[] LoadImage(string imgIndex)
{
    
if(conn.State !ConnectionState.Open ) conn.Open();
    
sSQL "SELECT ImageData FROM Images WHERE imgID = " + imgIndex;
    
OleDbCommand cmd = new OleDbCommand(sSQL, conn);
    byte
[] imgData (byte[]) cmd.ExecuteScalar();
    
conn.Close();
    return 
imgData;
}


Colorized by: CarlosAg.CodeColorizer

In any case, you can download a complete code example here, source code commented in English and Spanish. Please feel free to download it and play a little with it.


NOTE.- On a future entry I’ll tell you how to compact and repair a Microsoft Access database with C# code. Applications like the source code you can download from this post need that compact and repair capabilities, and they need it badly.

ReadOnly WinForms DataGrid

Windows Forms’ DataGrid control allows users, by default, to modify the data shown and even to create new records.

What if we want to make a grid that only shows data, without the power to edit o make new data?

For that, one of the things we can make is modifying the default properties of the DefaultView from the table we’re going to feed the grid with. For instance, let’s say we have a DataTable dtData, acting as the grid DataSource. If we modify the AllowDelete, AllowEdit and AllowNew properties from the default view of the table, like this:

dtData.DefaultView.AllowDelete = false;
dtData.DefaultView.AllowEdit = false;
dtData.DefaultView.AllowNew = false;
grdData.DataSource = this.dtDatos;

Colorized by: CarlosAg.CodeColorizer

the grdData data grid won’t allow users to modify, delete or add new records.

In the other hand, if we want to select the whole row of the grid with just clicking on any of its cells, we have to write the following code on the CurrentCellChanged datagrid event.

private void grdData_CurrentCellChanged(object sender, EventArgs e)
{
    grdData.Select(grdData.CurrentRowIndex)
;
}

Colorized by: CarlosAg.CodeColorizer

Quite simply, this code tells the datagrid to select the entire row, knowing which row is the one thanks to the CurrentRowIndex property.

Storing binary data on SQL Server

Mon 20 February, 2006

On my current project, an ASP .NET application, the customer expressed the desire to save in the database letters, based on Word templates and automatically generated by the application. Being official government letters, if in the future you need print a copy of that document it has to be exactly the same document, although some of the data on which the document was originally based could have changed. So they need them to be stored in the database exactly as they were generated once. Therefore, we need a way to store the Word files as a binary format, as a data stream to be able to reload them again in the future and have the exact same document.

Please note: it’s not the objective of this post to show how to create Word files from C#. You can use for it Word automatization, or third-party components such as the excellent ones made by Aspose.

Let’s create a simple SQL Server table to store the documents. For the sake of this example, we call it BinaryDocs, and it will have the following design:

Field Type Nullable?
DocId Int (identity) No
Document Image No
DocName VarChar(100) No

Pretty self-explanatory: DocId is an autonumeric primary key field. Document is the one where the bit stream is going to be stored, that is the proper Word document. DocName will store the original name the document had when it was generated.

After that will create an stored procedure, UploadDoc, which will handle the actual insertion of records on BinaryDocs:

CREATE PROCEDURE UploadDoc(@doc AS Image, @name AS VarChar(100)) 
AS
INSERT INTO 
BinaryDocs (Document, DocName) values (@doc, @name)
GO

Colorized by: CarlosAg.CodeColorizer

Quite self-explanatory too: a simple INSERT INTO instruction which receives an Image and a VarChar fields as parameters.

And this is the method that stores the data on the database:

private void saveDocToDB(string sPath, string sFile)
{
    
//New FileStream object for reading the Word file as a binary stream
    
System.IO.FileStream  fs = new FileStream(sPath + sFile,
        System.IO.FileMode.Open)
;
    
//On this byte array we’ll store the binary data read by fs.
    
Byte[] data = new byte[fs.Length];
    
//Like this
    
fs.Read(data, 0, Convert.ToInt32(fs.Length));
    
//Open up a SQL Server connection. In this particular case the database
    //connection string is recovered from the web.config file
    //using the ConfigurationSettings class
    
SqlConnection cnn =
       new 
SqlConnection(ConfigurationSettings.AppSettings["DBConnection"]);
    
cnn.Open();
    
//An StoredProcedure SqlCommand object to invoke the
    //UploadDoc stor