Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Portable Code Base

4.88/5 (10 votes)
23 Aug 2011CPOL3 min read 32.1K   1K  
Portable code snippet database

Introduction

This little Code Database utility can store code snippets and even provide syntax highlighting.

Background

I just hate it if I have to go scratch round in some of my other projects to find a code snippet or piece of source code. After searching online for a free, portable code database, I decided to code my own.

Using the Code

The utility makes use of DotNet Fireball 's Syntax Highlighter. I used to use ScintillaNet but it was limited in the number of languages it supported.

Changing the code language is pretty straightforward:

C#
private void SetLanguage(string syntax)
{
    int CodeSyntax = 0;

    try
    {
        CodeSyntax = (Int32)Enum.Parse(typeof(SyntaxEditor.enumSyntax), syntax);
        syntaxEditor1.CodeSyntaxIndex = CodeSyntax;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Since this is a portable app (only one EXE), I made use of XML as the database.

To create the structure of the database, I created a CreateNEWDB() method that just creates the columns, etc.

C#
private void CreateNewDB()
{
    DataColumn dcol;

    try 
    {
        //Create new Datatable
        dtCodeEntries = new DataTable("CodeEntry");

        //Create ID column.
        dcol = new DataColumn("ID",typeof(int));
        dcol.Caption = "ID";
        //Add column to Datatable
        dtCodeEntries.Columns.Add(dcol);

        //Create Name column.
        dcol = new DataColumn("Name", typeof(string));
        dcol.Caption = "Name";
        dcol.MaxLength = 99;
        //Add column to Datatable
        dtCodeEntries.Columns.Add(dcol);

        //Create Entry / Code column.
        dcol = new DataColumn("Entry", typeof(string));
        dcol.MaxLength = 8000;
        dcol.Caption = "Code Entry";
        //Add column to Datatable
        dtCodeEntries.Columns.Add(dcol);

        //Create Language column.
        dcol = new DataColumn("Language", typeof(string));
        dcol.Caption = "Language";
        dcol.MaxLength = 10;
        //Add column to Datatable
        dtCodeEntries.Columns.Add(dcol);

        codeDS.Tables.Add(dtCodeEntries);

        btnNew.Enabled = true;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }

}

The project uses an untyped DataSet that makes use of the ReadXML() method to populate it. Loading the database is also pretty straightforward:

C#
private void LoadExternalDB()
{
    if (dbPath != string.Empty)
    {
        try
        {
            //Reset the dataset
            codeDS = new DataSet();
            //Get the data from the XML database into our dataset
            codeDS.ReadXml(dbPath);
            //Populate the DataTable with all the entries
            PopulateDataTable();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
}

private void PopulateDataTable()
{
    if (codeDS != null)
    {
        if (codeDS.Tables.Count > 0)
        {
            //Clear our List
            CodeEntries.Clear();

            //Build up the list from the DataTable's Rows
            foreach (DataRow dr in codeDS.Tables[0].Rows)
            {
                CodeEntry ce = new CodeEntry();
                //setup the CodeEntry from the DataRow
                ce.Fill(dr);
                //Ad the CodeEntry to our List
                CodeEntries.Add(ce);
            }
        }
    }
}

To ease the Interface / Database transition, I make use of a List to store a CodeEntry. The CodeEntry is a class that resembles the database entry structure.

C#
public class CodeEntry
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Entry { get; set; }
    public string Language { get; set; }

    public CodeEntry()
    {
        ID = -1;
        Name = "";
        Entry = "";
        Language = "";
    }

    /// <summary>
    /// Fill the CodeEntry from the DataRow.
    /// </summary>
    /// <param name="dr">The DataRow to fill from</param>
    /// <remarks>
    /// This method is a bit over the top as it caters for future growth.
    /// A simple hardcoded method would suffice, but why do a bad job now...
    /// </remarks>
    public void Fill(System.Data.DataRow dr)
    {
        System.ComponentModel.PropertyDescriptorCollection props = 
               System.ComponentModel.TypeDescriptor.GetProperties(this);
        
        //Loop thru the properties
        for (int i = 0; (i < props.Count); i = (i + 1))
        {
            //Get the property details
            System.ComponentModel.PropertyDescriptor prop = props[i];
            //Don't try to set a read-only property.
            if ((prop.IsReadOnly != true))
            {
                try
                {
                    //Check that the data row value isn't null
                    if ((dr[prop.Name].Equals(System.DBNull.Value) != true))
                    {
                        //Check if the property is the same
                        //as the database type otherwise convert.
                        if ((prop.PropertyType.Equals(dr[prop.Name].GetType()) != true))
                        {
                            prop.SetValue(this, prop.Converter.ConvertFrom(dr[prop.Name]));
                        }
                        else
                        {
                            prop.SetValue(this, dr[prop.Name]);
                        }
                    }
                }
                catch (System.Exception)
                {
                }
            }
        }
    }
}

The Fill method of the CodeEntry makes it simple to populate the CodeEntry from a DataRow. This method might be a bit over the top but it makes it easy to add more properties to the class without rewriting.

Saving the database to XML is made easy with the WriteXML() method.

C#
private void SaveAndReload()
{
    try
    {
        //Convert the List to a DataTable so that we can save it to XML
        dtCodeEntries = ToDataTable(CodeEntries);
        dtCodeEntries.TableName = "CodeEntry";
        codeDS = new DataSet();
        codeDS.Tables.Add(dtCodeEntries);
        if (dbPath == string.Empty)
        {
            if (saveFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                dbPath = saveFileDialog1.FileName;
            }
        }
        //Save DB to XML
        codeDS.WriteXml(dbPath);
        
        //Reload Info
        LoadDB(false);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

This SaveAndReload method is used when saving and deleting as it converts our List of CodeEntrys to a DataTable. This DataTable is then added to the DataSet which in turn calls the WriteXML() method to save our database to XML. The WriteXML method also exists on the DataTable object, but for future development, we might want more DataTables to perhaps store categories, etc.

A problem was found after the initial release with the ScintillaNet control where it required SciLexer.dll. (Thanks Petr Kohout.)

To keep the app portable, I made some alterations to extract these .dlls at startup.

C#
private void ExtractDLLs()
{
    try
    {
        //View embedded resource names
        //Assembly asm = this.GetType().Assembly;
        //string[] resNames = asm.GetManifestResourceNames();

        List<byte[]> EmbeddedDlls = new List<byte[]>();

        EmbeddedDlls.Add(Properties.Resources.Fireball_CodeEditor);
        EmbeddedDlls.Add(Properties.Resources.Fireball_CodeEditor_SyntaxFiles);
        EmbeddedDlls.Add(Properties.Resources.Fireball_Core);
        EmbeddedDlls.Add(Properties.Resources.Fireball_SyntaxDocument);
        EmbeddedDlls.Add(Properties.Resources.Fireball_Win32);
        EmbeddedDlls.Add(Properties.Resources.Fireball_Windows_Forms);

        int index = 0;

        startPath = Application.StartupPath;
        string[] dllNames = new string[] { "Fireball.CodeEditor", 
          "Fireball.CodeEditor.SyntaxFiles", "Fireball.Core", 
          "Fireball.SyntaxDocument", "Fireball.Win32", 
          "Fireball.Windows.Forms" };

        foreach (byte[] dllBuffer in EmbeddedDlls)
        {
            string dllName = dllNames[index];
            if (!File.Exists(startPath + @"\" + dllName + ".dll"))
            {
                CreateFileFromByteArray(dllBuffer, startPath + @"\" + dllName + ".dll");
            }
            index++;
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

There are better ways of getting back the embedded resources (DLLs), but I was having problems with the resource manager. It kept searching for the resources in CodeBasePortable.Resources.resources. I used the assembly to show me the resource names so that I could specify the exact name, but it still didn't retrieve it.

C#
//View embedded resource names
Assembly asm = this.GetType().Assembly;
string[] resNames = asm.GetManifestResourceNames();

I had to resort to violence and go the Properties.Resources route to extract the DLLs.

After getting the file from the resource manager, we need to write the .dll to the startup path.

C#
public static void CreateFileFromByteArray(byte[] barrFile, string strDestinationPath)
{
    try
    {
        FileStream output = new FileStream(strDestinationPath, 
                                FileMode.Create, FileAccess.Write);
        BinaryWriter writer = new BinaryWriter(output);
        writer.Write(barrFile);
        writer.Close();
        output.Close();
    }
    catch (Exception exception1)
    {
        MessageBox.Show("There was a problem writing " + 
                   "the file to the path " + strDestinationPath);
    }
}

Points of interest

The class CodeEntry makes it easy to manipulate code entries. Filling them is easy and future proof.

The main project features a method to convert a List to a DataTable for easy saving of data.

The ScintillaNET syntax highlighting control made it simple to highlight code but proved to be limited. I went with Dotnet Fireball as it has plenty of code language support instead.

I also included the project for the CodeBase add-in. This is a Visual Studio 2010 add-in that can be launched from the IDE. The great advantage here is you can now have a Code Base as part of your development environment and carry the code database around with CodeBase Portable.

Possible solution problems

Since I chucked out ScintillaNEt, there shouldn't be any solution problems, but please inform me of any so that I can fix them.

History

First version used ScintillaNet for syntax highlighting but proved limited. New version uses DotNet Fireball instead.

This is still pretty straightforward. Later revisions might include categories, etc.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)