Tuesday, March 26, 2013

How to find the Dropbox directory programmatically in C#

If you need to know where the user puts the Dropbox directory you could just ask him/her and put a textbox in the settings for them to fill out. But it's bothersome and inconvenient, and the less we could ask the user the better.
Dropbox puts a file in the ApplicationData directory, called "host.db", which contains the Dropbox path in the second line. It's fairly simple to access that data, we just read the whole file (which is small), than we put the second line in a byte array converting it, and in the end we just put the string in a variable, converting in ASCII. It is simple and effective, doesn't require much time, and you can easily detect if an user has Dropbox installed or not by just checking the file existence in the default path. Here's the code:
string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string dropboxPath = System.IO.Path.Combine(appData, "Dropbox\\host.db");
string[] lines = System.IO.File.ReadAllLines(dropboxPath);
byte[] dropboxBase64 = Convert.FromBase64String(lines[1]);
string folderPath = System.Text.ASCIIEncoding.ASCII.GetString(dropboxBase64);

How to zoom a picturebox with the mouse in C#

Zooming a picturebox with the mouse wheel is something really useful, and it's achieved with the OnMouseWheel event of the picturebox. But the picturebox doesn't have an OnMouseWheel event you can set, that means you need to override the standard picturebox method. This is a really simple operation, you can just create a method with this signature:
protected override void OnMouseWheel(MouseEventArgs mea)
Then you can put all the zoom code in there.
The only thing you need to understand the zoom code is basic math. The zoom works by defining a minumum/maximum magnification and by defining by how much you want to zoom at every movement of the mouse wheel.
In this example the min/max will be 15, and the zoom factor will be 1.25. We will NOT change the image in any way, the only thing that gets bigger or smaller is the picturebox itself, that's why i suggest you set your picturebox size mode to "Zoom".
Here's a simple implementation:
protected override void OnMouseWheel(MouseEventArgs mea)
{
    // Override OnMouseWheel event, for zooming in/out with the scroll wheel
    if (pictureBox1.Image != null)
    {
        // If the mouse wheel is moved forward (Zoom in)
        if (mea.Delta > 0)
        {
            // Check if the pictureBox dimensions are in range (15 is the minimum and maximum zoom level)
            if ((pictureBox1.Width < (15 * this.Width)) && (pictureBox1.Height < (15 * this.Height)))
            {
                // Change the size of the picturebox, multiply it by the ZOOM FACTOR
                pictureBox1.Width = (int)(pictureBox1.Width * 1.25);
                pictureBox1.Height = (int)(pictureBox1.Height * 1.25);
            }
        }
        //Zoom out
        else
        {
            // Check if the pictureBox dimensions are in range (15 is the minimum and maximum zoom level)
            if ((pictureBox1.Width > (this.Width / 15)) && (pictureBox1.Height > (this.Height / 15)))
            {
                // Change the size of the picturebox, divide it by the ZOOM FACTOR
                pictureBox1.Width = (int)(pictureBox1.Width / 1.25);
                pictureBox1.Height = (int)(pictureBox1.Height / 1.25);
            }
        }
    }
}
This code will work and it will zoom your picturebox. There is only a little problem, depending on how you want your picturebox to behave. In this code the picturebox will always stay in the same position, this means that the upper left corner will be in a fixed position, and everything else will be shifting. If you want the picturebox to follow your mouse cursor while you zoom, you need to add a little formula. This will do the trick for the Zoom In part:
pictureBox1.Top = (int)(mea.Y - 1.25 * (mea.Y - pictureBox1.Top));
pictureBox1.Left = (int)(mea.X - 1.25 * (mea.X - pictureBox1.Left));
And this is what you need to add in the Zoom Out part:
pictureBox1.Top = (int)(mea.Y - 0.80 * (mea.Y - pictureBox1.Top));
pictureBox1.Left = (int)(mea.X - 0.80 * (mea.X - pictureBox1.Left));
As you can see, only the zoom factor changes. Let's put everything together and will get this:
protected override void OnMouseWheel(MouseEventArgs mea)
{
    // Override OnMouseWheel event, for zooming in/out with the scroll wheel
    if (pictureBox1.Image != null)
    {
        // If the mouse wheel is moved forward (Zoom in)
        if (mea.Delta > 0)
        {
            // Check if the pictureBox dimensions are in range (15 is the minimum and maximum zoom level)
            if ((pictureBox1.Width < (15 * this.Width)) && (pictureBox1.Height < (15 * this.Height)))
            {
                // Change the size of the picturebox, multiply it by the ZOOMFACTOR
                pictureBox1.Width = (int)(pictureBox1.Width * 1.25);
                pictureBox1.Height = (int)(pictureBox1.Height * 1.25);

                // Formula to move the picturebox, to zoom in the point selected by the mouse cursor
                pictureBox1.Top = (int)(mea.Y - 1.25 * (mea.Y - pictureBox1.Top));
                pictureBox1.Left = (int)(mea.X - 1.25 * (mea.X - pictureBox1.Left));
            }
        }
        else
        {
            // Check if the pictureBox dimensions are in range (15 is the minimum and maximum zoom level)
            if ((pictureBox1.Width > (this.Width / 15)) && (pictureBox1.Height > (this.Height / 15)))
            {
                // Change the size of the picturebox, divide it by the ZOOMFACTOR
                pictureBox1.Width = (int)(pictureBox1.Width / 1.25);
                pictureBox1.Height = (int)(pictureBox1.Height / 1.25);

                // Formula to move the picturebox, to zoom in the point selected by the mouse cursor
                pictureBox1.Top = (int)(mea.Y - 0.80 * (mea.Y - pictureBox1.Top));
                pictureBox1.Left = (int)(mea.X - 0.80 * (mea.X - pictureBox1.Left));
            }
        }
    }
}

Monday, March 25, 2013

A generic error occurred in GDI+

This is a common exception you'll get when trying to delete or rename an image file. The problem is that the method to load the image from file will put a lock on the file, so you can't do anything with it until you dispose the image.
There are two ways around that: either load the image from a stream or dispose the image before deleting or renaming the file.
Loading the image from stream works, but the actual loading is a bit slower and you need to keep the stream open until you don't need the image anymore. You can't just load it with a stream, close it, and then work with the image. It might work with some file format, but the documentation itself tells you to keep the stream open. Here's how to do it:
using (FileStream stream = new FileStream(@"path\to\image", FileMode.Open, FileAccess.Read))
{
    pictureBox1.Image = Image.FromStream(stream);
}
The problem here is that after assigning the image to the picturebox the stream will close ("using" statement). You could do this instead:
FileStream stream = new FileStream(@"path\to\image", FileMode.Open, FileAccess.Read);
pictureBox1.Image = Image.FromStream(stream);
But then you'll have an open stream forever, until you close it, which is not really a good solution. The real solution comes from loading the image from file, like this:
pictureBox1.Image = Image.FromFile(@"path\to\image");
And then dispose it correctly.
I'm sure you know that we should always dispose everything disposable after we're done with it, so it shouldn't be a surprise, but the problem in disposing and then deleting or renaming soon after is that the image has not enough time to be disposed. So, if you do this:
pictureBox1.Image.Dipose();
File.Delete(@"path\to\image");
You'll most likely incur in the "A generic error occurred in GDI+" exception, because the lock on the file is not yet released.
The REAL and FINAL solution to this, is to call the garbage collector before deleting the file.
You can do that by writing this:
GC.Collect();
After disposing the image.
There's still a problem though, the Collect() method is called asynchronously, that means you could and probably will incur in the same error as before, since the file could still be locked. You need to tell the program to wait for the garbage collector to finish, by adding this:
GC.WaitForPendingFinalizers();
After the GC.Collect().
So, the final code would be:
pictureBox1.Image.Dipose();
GC.Collect();
GC.WaitForPendingFinalizers();
File.Delete(@"path\to\image");
And that's how you delete or rename an image file safely in C#.

Searching algorithms in C#

Let's just get this out of the way: we all know that in C# the BinarySearch is already implemented, so this is more of a didactic exercise than a real world application. Also, the C# implementation is most likely optimized to death, so the point of this article isn't to make it better, i'm just going to show you what it really does.
If you're only interested in searching something in a list and be done with it, you can stop after the first line of code.
If you just began your Computer Science course, read everything, because it will be useful to you (searching algorithms are important, just in case you didn't know).
The C# way of searching a sorted list/array is the following:
int index = array.BinarySearch(Element);
Simple, straightforward, fast.

Having that out of the way, what would be your first thought if i asked you to search a specific element in a list? You would probably think of a linear search. A linear search algorithm is pretty much this:
1 - Compare the 1 element to the search term -> it's the same? If yes, stop, if not, go on.
2 - Compare the 2 element to the search term -> it's the same? If yes, stop, if not, go on.
3 - Compare the 3 element to the search term -> it's the same? If yes, stop, if not, go on.
etc etc.
For this algorithm to work you don't need the list to be sorted, you're going to read it all until you find what you want anyway. And this is pretty much the only advantage it has over the binary search algorithm. Here's the C# implementation:
public static int linearSearch(int[] array, int searchTerm)
{
    int index = -1;
    for (int i = 0; i < array.Length - 1; i++)
    {
        if (array[i] == searchTerm)
        {
            index = i;
            break;
        }
    }
    return index;
}
This function returns the index of the found element, it's really easy to write, but it's inefficient. In the worst case scenario you need to cycle through the whole array to reach the end. In a big array with, let's say, 100000 elements you'd be doing 100000 iterations. That means the efficiency in the worst case scenario is n, where n is the number of elements in the array. The best case efficiency is 1, and the average is n/2.

So, can we improve the efficiency? Of course we can, the binary search algorithm is the solution.
I can't stress this enough, but the array/list NEEDS to be sorted for the binary search to work.

The binary search could be described as the following, using a dictionary as an example:
1 - You need to search a word in the dictionary, the word is "viewer".
2 - The first thing you do is opening the dictionary in the middle.
3 - "viewer" starts with the letter "v".
4 -  "v" is on the second half of your dictionary (first half is a - m, second half is n - z)
5 - Then you open the second half of the dictionary in the middle.
6 - Repeat until you find the word you're looking for.
Putting the algorithm in code is simple once you really understand it:
public static int binarySearch(int[] array, int searchTerm)
{
    int firstIndex = 0;
    int lastIndex = array.Length;

    //Return -1 if the searchTerm is bigger or smaller than the first or last array element
    //This check avoids unnecessary computation in some cases
    if (searchTerm > array[array.Length - 1] || searchTerm < array[0])
    {
        return -1;
    }

    while (firstIndex <= lastIndex)
    {
        //Here we split the array in two and we search only the half we need
        int middle = (firstIndex + lastIndex) / 2;
        //Second half
        if (searchTerm > array[middle])
        {
            firstIndex = middle + 1;
        }
        //First half
        else if (searchTerm < array[middle])
        {
            lastIndex = middle - 1;
        }
        //element found, return
        else
        {
            return middle;
        }
    }

    return -1; //We only get here if the element hasn't been found
}
It's worth noting that we are sorting an array of integers, because it's easier to understand, but you can adapt the method to a string easily, using:
string.Compare(a, b);
This is much faster than the linear search, in the worst case scenario (in an array of 100000 elements) it will do just 16 comparisons instead of the whole 100000, so the efficiency would be log2n.
But can we make it better? We could use recursion!
public static int binarySearch(int[] array, int searchTerm, int firstIndex, int lastIndex)
{
    //Element not found, exit failsafe
    if(lastIndex < firstIndex)
    {
        return -1; 
    }

    //Here we split the array in two and we search only the half we need
    int middle = (lastIndex + firstIndex) / 2;
    
    //Second half
    if (searchTerm > array[middle])
    {
        //Recursively call this method with the new indexes
        return binarySearch(array, searchTerm, middle + 1, lastIndex);
    }
    //First half
    else if (searchTerm < array[middle])
    {
        //Recursively call this method with the new indexes
        return binarySearch(array, searchTerm, firstIndex, middle - 1);
    }
    //Element found, return
    return middle; 
}
This isn't really better, since the algorithm is still the same, but it's a more elegant solution and it's probably the one that has most in common with the standard C# implementation.
That's it, hopefully you now know a bit more about searching algorithms.

Sunday, March 24, 2013

How to extract a ZIP or RAR file in C#

The .NET framework has some archives functionality with the deflate class, but i found that it's not versatile enough, and it's a bit of a pain to use.
There's a lot of libraries that enables you to handle archive files in an easy way, personally i found that SharpCompress is the best. It's small, open source, it works well and the author is a great guy. Reading the documentation is enough to learn how to extract everything. To start using it you need to add a reference to the sharpcompress.dll in your project, you can do that by:
Right click on the project name in Visual Studio -> Add reference -> Browse -> Select the .dll from the folder you saved it in.
Once you do that, you can start using the library by telling your program what you want to use. You should already know how, but here's what you need for this method to work.
using SharpCompress.Common;
using SharpCompress.Archive;
After you do that you're ready to extract some files, the method is really simple, you just tell SharpCompress which file you want to extract, and you cycle through every file in the archive to save it to a directory.
private void extractArchive(string dir, string file) 
{
    var compressed = ArchiveFactory.Open(@file);
                    
    foreach (var entry in compressed.Entries)
    {
        if (!entry.IsDirectory)
        {
            entry.WriteToDirectory(@dir, ExtractOptions.ExtractFullPath | ExtractOptions.Overwrite);
        }
    }
}
This would be enough to extract archives, but it doesn't show you the progress, and the user would be presented with a stalled UI if you don't run the code in a separate thread. I'm not gonna cover multithreading right now, but a simple progress bar would be a really nice addition for the users. Implementing a progress bar in C# is really easy, you just create the object, put it wherever you want, set a maximum amount and at every iteration of the cycle you increment the progress bar value by one. I'm gonna show a simple example of a progress bar that will be showed at the center of the form when you extract a file:
using SharpCompress.Common;
using SharpCompress.Archive;

private void extractArchive(string dir, string file) 
{
    var compressed = ArchiveFactory.Open(@file);
    //Creates the ProgressBar object
    ProgressBar bar = new ProgressBar();
    //Assing the ProgressBar dimensions, change it however you like
    bar.Height = 50;
    bar.Width = 200;
    //Set the ProgressBar location, in this case in the center form
    bar.Location = new Point(this.Width / 2 - bar.Width / 2, this.Height / 2 - bar.Height / 2);
    //Set the maximum value for the ProgressBar
    bar.Maximum = compressed.Entries.Count() * 10;
    //Add the ProgressBar to the form controls
    this.Controls.Add(bar);

    foreach (var entry in compressed.Entries)
    {
        if (!entry.IsDirectory)
        {
            entry.WriteToDirectory(@dir, ExtractOptions.ExtractFullPath | ExtractOptions.Overwrite);
        }
        //Increment the ProgressBar value by 1
        bar.PerformStep();
    }
    //Remove the ProgressBar from the form and dispose
    this.Controls.Remove(bar);
    bar.Dispose();
}
You can obviously change the ExtractOptions to whatever will suit your needs.

Resize an image in C#

Resizing an image is something that can be very useful, even if you aren't writing a specific application that works mostly with images. I'll post three methods to resize an image, in different ways, all of them useful. All three are really easy to understand and to implement.

Resing an image, regardless of the aspect ratio: this is the easiest, can be useful in some situations. Even though you might want to preserve the aspect ratio most of the time, it's a good thing to have the option of resizing without constraints.
public static Image resize(Image img, int width, int height)
{
    // Resize image with the size from the parameters
    Image bmp = new Bitmap(width, height);
    Graphics g = Graphics.FromImage(bmp);
    // The interpolation mode is optional, you can choose whatever you like
    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
    g.DrawImage(img, 0, 0, width, height);
    g.Dispose();
    return bmp;
}

Resizing an image, maintainig the aspect ratio, based on percentage. Been able to resize an image sending only the image and a percentage as parameters is really useful, because sometimes that's all you need.
public static Image resize_percentage(Image img, int percentage)
{
    // Resize the image from the percentage parameter
    // Calculate the new dimensions
    int resizedWidth = (img.Width * percentage / 100);
    int resizedHeight = (img.Height * percentage / 100);
    // Make a new bitmap with the new dimensions
    Image bmp = new Bitmap(resizedWidth, resizedHeight);
    // Create the Graphics object to draw on the image
    Graphics g = Graphics.FromImage(bmp);
    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
    g.DrawImage(img, 0, 0, resizedWidth, resizedHeight);
    // Always remember to Dispose!
    g.Dispose();
    return bmp;
}

Resizing an image maintaining the aspect ratio, based on actual dimensions. This is one of the most useful methods you'll use when dealing with images.
public static Image resizeWithAR(Image image, int maxWidth, int maxHeight)
{
    // Calculate the ratio to maintain aspect ratio
    double ratioX = (double)maxWidth / image.Width;
    double ratioY = (double)maxHeight / image.Height;
    double ratio = Math.Min(ratioX, ratioY);

    // Calculate the new dimensions
    int newWidth = (int)(image.Width * ratio);
    int newHeight = (int)(image.Height * ratio);

    // Draw the new image
    Image returnImg = new Bitmap(newWidth, newHeight);
    Graphics g = Graphics.FromImage(returnImg);
    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
    g.DrawImage(image, 0, 0, newWidth, newHeight);
    g.Dispose();

    return returnImg;
}

Saturday, March 23, 2013

Batch rename with leading zeroes

Let's say you're writing a batch rename function in C#, but you want the number in the filenames to have a leading zero, like this:
filename-01
filename-02
filename-03
etc. etc.

You need to find how many zeroes you need to add, based on the number of the total files, or you would end up with something like this:
filename-09
filename-010
filename-011
etc. etc.
Which looks bad, and it could give problems with some programs depending on how they sort a file list.
The result you want is one where the number's count is the same, indipendently from the number of files, even if you have a million files, it needs to work.
Doing an if/else is bad, because you would need to know the number of files before-hand. The solution is pretty simple, this is the result you will get:

filename-08
filename-09
filename-10
filename-11

Or, using larger numbers:

filename-0001
filename-0002
...
filename-0099
filename-0100
...
filename-0999
filename-1000
etc. etc. 
Let's see the code:

List<string> files = new List<string>(); //Our list of files

//populate the file list in any way you want, like this
private void populateFiles() 
{
    string dir = @"path\to\your\directory";
    foreach(string s in Directory.GetFiles(dir)) 
    {
        files.Add(s);
    }
    //Or you could do a files.AddRange(Directory.GetFiles(dir));
}

private void batchRename() 
{
    //Cycle every file in the list
    for (int i = 0; i < files.Count; i++)
    {
        //zeroesAmount contains the number of zeroes to add to the title
        //Let's say the files.Count is 758 and "i" is now at 19
        //We get the length of the string "758", which is 3
        //We get the length of the string "19", which is 2
        //We subtract them, 3 - 2 = 1, so it's 1 zero to add
        int zeroesAmount = files.Count.ToString().Length - i.ToString().Length;

        //This is the string that will be added to the file name
        string zeroes = "";

        //Actual cycle to add the zeroes number to the string
        for (int ii = 0; ii < zeroesAmount; ii++)
        {
            zeroes += "0";
        }

        //Assigning the new file name and renaming, with the File.Move function
        string parent = Directory.GetParent(files[i]);
        string fileName = Path.GetFileNameWithoutExtension(files[i]);
        string extension = Path.GetExtension(files[i]);
        string newFile = parent + "\\" + fileName + "-" + zeroes + i.ToString() + extension;
        if (!File.Exists(newFile))
        {
            File.Move(files[i], newFile);
        }
    }
}

prettyprint