Virtual Photo Album in C#


Before I start my third article, let me thank to Chandana Subasinghe for his Article about the TreeView. I was looking to create my application using drive and file controls, but because I found his code, and it was easy to understand, I took it and enhanced it and implemented into my application. After all, I think we should do that often to learn new things.

Introduction:

This application called "Virtual Photo Album". I didn't use any third party components, so it should be easy for you to download the code and play with it. I will try not to use any third party components while I am learning C#.

Features:
  • Locate and create separate album files of any images in your computer or combination of network computers.
  • Rotate and save images
  • Start Slideshows for any album you selected

The idea of this program is that, if you do have many photos and just want to show a few from here and a few from there to your friends and families... you don't have to move them into one folder, instead you can make a list of the images you want to show to different people. It creates different files with ".vpa" extension (Visual Photo Album) which are text files go into "data" folder, which also sits in the application folder. Even if your photos are organized very well, it's just to make things easier if you want to combine a few photos from separate locations.

Background:

There are no Databases but only files to keep the path of the images. Each named differently and can be accessed through the drop down in the menu bar. Even though I showed some basic codes in my past articles, I would like to refresh some of them here. Since this is the beginner section, there is no harm to repeat stuff.

Using the code:

This program has less items in it. Only one form and a class module.

  • Form1 (Main Form)
  • FileExplorer.cs (Class module that does TreeView loading) I kept its original name from Chandana Subasinghe's article.

Since the whole code is available for download, I will start with some simple stuff, and probably stay simple in this entire article. Please feel free to play with the code, and see if you can come up something better.

private void FileListCheck()
 {
     if (Directory.Exists(@"data") == false) // Create data folder if not exist yet
 
    {
         Directory.CreateDirectory(@"data");
         WriteReadmeText(); // Create 'Readme' file
 
    }
     else
 
    {
         if (File.Exists(@"data\\readme.txt") == false) {
             WriteReadmeText(); }
     }
     string[] MyFiles = Directory.GetFiles(@"data","*.vpa"); // Check the folder if any
 
                                                // 'vpa' files exist (Virtual Photo Album file)
 
    if (MyFiles.Length > 0) // check if any files int he directory
 
    {
         char Splitchr = (char)92; // Slash character
 
        string[] SplitText;
         TSFiles.Items.Clear();
         foreach (string ReadFiles in MyFiles)
         {
             SplitText = ReadFiles.Split(Splitchr);
             TSFiles.Items.Add(SplitText.GetValue(SplitText.GetUpperBound(0)));
         }
     }

 }

'Directory.Exists' and 'File.Exists' here from System.IO namespace checks if my folder and file already exist. In my past applications, I used SystemFile object to do that, I honestly didn't know System.IO was available for me.

You can also see how 'Split' is done and how I actually used 'GetUpperBound' of the 'Split' method to fill my ListView's 'Name' field.

private void WriteReadmeText() // Create 'ReadMe text file'
 
{
     StreamWriter ReadMeFile;
     ReadMeFile = new StreamWriter(@"data\\readme.txt");
     ReadMeFile.WriteLine("Please do not delete files in this folder.");
     ReadMeFile.WriteLine("This folder used by 'Virtual Photo Album' - Copyrights (2010)");
     ReadMeFile.WriteLine("If you'd like more information about the software and/or its author,");
     ReadMeFile.WriteLine("please contact '[email protected]' or click on 'help' button on the program.");
     ReadMeFile.Close();

 }

A very simple 'File Write' method. Here I am creating my 'Readme.txt' file. Sometimes we should leave some sort of clues to people what the files are in our application folders, so they won't play with them and break the program.

private void fleExp_AfterSelect(object sender, TreeViewEventArgs e)
 {
     picBox.ImageLocation = "";
     if (fleExp.SelectedNode.ImageIndex == 3) {
         picBox.ImageLocation = fleExp.SelectedNode.FullPath; }

 }

Here I used (cheat) 'ImageIndex' to find out my images in TreeView to show them on the PictureBox. All image files that I can show in the PictureBox get the same icon, which is #3 here. 'fleExp.SelectedNode.FullPath' gets me the whole path for the file.

private void ImgRotation(string IRotateValue, string ICurrentImagePath)
 {
     try
 
    {
         if (picBox.ImageLocation == @"missing.png") {
             MessageBox.Show("Can not rotate default image", "Invalid Image",
                 MessageBoxButtons.OK, MessageBoxIcon.Information);
             return; } // 'missing.png' is a default image can't be modified
 
        else {
             Bitmap Rotate_Bitmap = new Bitmap(ICurrentImagePath);
             switch (IRotateValue) {
                 case "0": // Reset
 
                    Rotate_Bitmap.RotateFlip(RotateFlipType.RotateNoneFlipNone);
                     break;
                 case "1": // 90 right
 
                    Rotate_Bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
                     break;
                 case "2": // 90 left
 
                    Rotate_Bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
                     break;
                 case "3": // Flip
 
                    Rotate_Bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);
                     break; }
                 picBox.Image = Rotate_Bitmap; }
     }
     catch
 
    {
         MessageBox.Show("There is no image on the viewer", "Empty Viewer",
                         MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
     }

 }

This method does the image rotation which called from the menu buttons. If you look at it, it's very simple, even for a PictureBox... but, everything is easy after we learn them ;) 'RotateFlipType' has other properties that you might want to try for your own codes.

private void TSFiles_SelectedIndexChanged(object sender, EventArgs e)
 {
     // Open selected Album file and show its list in the Album List
 
    lstView.Items.Clear();
     StreamReader OpenAfile = new StreamReader(@"data\\" + TSFiles.Text.Trim());
     char Splitchr = (char)92;
     string[] SplitText;
     string LineData;
     while (OpenAfile.EndOfStream != true)
     {
         LineData = OpenAfile.ReadLine();
         ListViewItem s_Items;
         s_Items = lstView.Items.Add(Convert.ToString(LineData));
         SplitText = Convert.ToString(LineData).Split(Splitchr);
                 s_Items.SubItems.Add(Convert.ToString(SplitText.GetValue(SplitText.GetUpperBound(0))));
     }
     picBox.ImageLocation = ""; // Clear the picture box
 
    SSLabel.Text = "Album Images : " + Convert.ToString(lstView.Items.Count);

 }

Since I am repeating things in this article, this method has Split, once again, and how to enter data into ListView columns (in detailed mode). If you red my other two articles, you'd know how much I love to work with ListView control. I can keep many things in there that saves me tons of coding and probably memory too. Here I kept the full path of the image in the first column, which its width set to '0' so you can't see it, and the file name for the second column... the only thing visible in the Album list.

private void TSSaveimage_Click(object sender, EventArgs e)
 {
     // Save images after rotated. This section should be fixed. it saves the
 
    // images although somehow changes image size.
 
    string[] SplitText;
     SplitText = picBox.ImageLocation.Split('.');
     switch (Convert.ToString(SplitText.GetValue(SplitText.GetUpperBound(0))).ToLower())
     {
         case "jpg":
             picBox.Image.Save(picBox.ImageLocation, System.Drawing.Imaging.ImageFormat.Jpeg);
             break;
         case "tif":
             picBox.Image.Save(picBox.ImageLocation, System.Drawing.Imaging.ImageFormat.Tiff);
             break;
         case "gif":
             picBox.Image.Save(picBox.ImageLocation, System.Drawing.Imaging.ImageFormat.Gif);
             break;
         case "png":
             picBox.Image.Save(picBox.ImageLocation, System.Drawing.Imaging.ImageFormat.Png);
             break;
     }

 }

This is something new. If you do work or would like to work with PictureBoxes, here is how you can save the files in the PictureBox after some work done. In my case, rotation is the work. There is 'file size' problem though. It compresses the file when saves. I didn't want to spend much more time for this, I let you find out the solution :)

private void TSDeleteFile_Click(object sender, EventArgs e)
 {
     // Deletes entire Album file.
 
    if (TSFiles.Text == "Select/Enter Album") {
         MessageBox.Show("select a file first!", "Selection missing",
             MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; }
     if (MessageBox.Show("Are you sure?", "Delete File", MessageBoxButtons.YesNo,
         MessageBoxIcon.Question) == DialogResult.No) { return; }
     lstView.Items.Clear();
     picBox.ImageLocation = "";
     File.Delete(@"data\\" + TSFiles.Text);
     FileListCheck(); TSFiles.Text = "Select/Enter Album";
     SSLabel.Text = "Album Images : 0";

 }

This is how to delete a file. Obviously very simple 'File.Delete(FilPath)' but also wanted to show you my indentations. Which is more compact if you see the whole code.

private void EnableDisableControls(bool StartStop)
 {
    // When SlideShow starts, most of the controls should be Disabled
 
   // and Enabled back when its stoped
 
   TSNewFile.Enabled = StartStop;
    TSDeleteFile.Enabled = StartStop;
    TSPlay.Enabled = StartStop;
    TSStop.Enabled = !StartStop;
    TSRotLeft.Enabled = StartStop;
    TSRotRight.Enabled = StartStop;
    TSFlip.Enabled = StartStop;
    TSReload.Enabled = StartStop;
    TSFit.Enabled = StartStop;
    TSSaveimage.Enabled = StartStop;
    TSEnterImage.Enabled = StartStop;
    TSRemoveImage.Enabled = StartStop;
    TSFiles.Enabled = StartStop;
    fleExp.Enabled = StartStop;
    lstView.Enabled = StartStop;

 }

When slideshow starts, most of the controls need to be disabled and abled back when stopped. Now, I wrote this method to save tons of lines, but check out the  'TSStop.Enabled = !StartStop'... boolean switches from 'true' to 'false' or from 'false' to 'true' because I need that control disabled and enabled apposite from the other controls :)

    int icons = 0;
     foreach (FileInfo file in rootDir.GetFiles())
     {
         TreeNode node = new TreeNode();
         node.Text = file.Name;
         // Set File icons. This portion can be larger depending on how many
 
        // files you want to have different icons for
 
        switch (file.Extension.ToLower())
         {
             case ".jpg": case ".gif": case ".tif": case ".png": // 4 image files
 
                icons = 3;
                 break;
             case ".txt": case ".rtf":
                 icons = 7;
                 break;
             case ".pdf":
                 icons = 8;
                 break;
             case ".doc": case ".docx":
                 icons = 4;
                 break;
             case ".mdb": case ".accdb":
                 icons = 5;
                 break;
             case ".exe": case ".dll":
                 icons = 9;
                 break;
             case ".zip": case ".rar":
                 icons = 10;
                 break;
             case ".bmp":
                 icons = 12;
                 break;
             default:
                 icons = 11;
                 break;
         }
         node.ImageIndex = icons;
         node.SelectedImageIndex = icons;
         parentNode.Nodes.Add(node);
     }

 }

Please see the whole code for TreeView creation. I only would like to show this portion which sets the icons of the items in the TreeView. As I mentioned at the beginning of the article, I kind of cheated to find out which files are images so I can show them in PictureBox... #3 icon for all the image files which can be loaded into the PictureBox control. There might be other files too, but 4 format is enough fo this program. 'node.SelectedImageIndex = icons'  is the key here. If you don't put that line of code, you would end up having different icons showing on the clicked items of the TreeView.

private void TSExit_Click(object sender, EventArgs e)
 {
     // Exit the program
 
    if (Application.MessageLoop)
     {
         // Use this since we are a WinForms app
 
        Application.Exit();
     }
     else
 
    {
         // Use this since we are a console app
 
        Environment.Exit(1);
     }

 }

And this is my application exit routine. I saw it somewhere and made sense to me to use it altogether like this.

Last few words

Try to close anything you open in your code. Try to use Exception catchers (Try..catch) to make your codes stronger. Comment your code whenever you have time and organize your code when you think your brain is getting slower... Commenting and organizing your code while relaxing your brain helps you go through al the code once again and keeps you fresh with the whole code.... and sometimes helps you to find bugs before you actually run the code.

Points of Interest:

C# is one of the more fun languages (my second, so this is the best one after VB). I enjoy each moment of my programs when actually works (well, hell yeah)... but I also enjoy trying to find the answers for my problems as well. These articles so far only for beginners just like me. I will be starting bit more better coding, some more difficult coding. I hope you follow me and take advantage of my codes to enhance yours, like I did with this one... or, just let me know what else I can do to make my codes better ;)

Download the whole code, and test it. If you like the idea, enhance it and make it your own. There are many other functions and controls can be put into this application, or just have the idea and write your code from scratch.