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:
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.
private void CreateNewDB()
{
DataColumn dcol;
try
{
dtCodeEntries = new DataTable("CodeEntry");
dcol = new DataColumn("ID",typeof(int));
dcol.Caption = "ID";
dtCodeEntries.Columns.Add(dcol);
dcol = new DataColumn("Name", typeof(string));
dcol.Caption = "Name";
dcol.MaxLength = 99;
dtCodeEntries.Columns.Add(dcol);
dcol = new DataColumn("Entry", typeof(string));
dcol.MaxLength = 8000;
dcol.Caption = "Code Entry";
dtCodeEntries.Columns.Add(dcol);
dcol = new DataColumn("Language", typeof(string));
dcol.Caption = "Language";
dcol.MaxLength = 10;
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:
private void LoadExternalDB()
{
if (dbPath != string.Empty)
{
try
{
codeDS = new DataSet();
codeDS.ReadXml(dbPath);
PopulateDataTable();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
private void PopulateDataTable()
{
if (codeDS != null)
{
if (codeDS.Tables.Count > 0)
{
CodeEntries.Clear();
foreach (DataRow dr in codeDS.Tables[0].Rows)
{
CodeEntry ce = new CodeEntry();
ce.Fill(dr);
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.
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 = "";
}
public void Fill(System.Data.DataRow dr)
{
System.ComponentModel.PropertyDescriptorCollection props =
System.ComponentModel.TypeDescriptor.GetProperties(this);
for (int i = 0; (i < props.Count); i = (i + 1))
{
System.ComponentModel.PropertyDescriptor prop = props[i];
if ((prop.IsReadOnly != true))
{
try
{
if ((dr[prop.Name].Equals(System.DBNull.Value) != true))
{
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.
private void SaveAndReload()
{
try
{
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;
}
}
codeDS.WriteXml(dbPath);
LoadDB(false);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
This SaveAndReload
method is used when saving and deleting as it converts our List
of CodeEntry
s 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 DataTable
s 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.
private void ExtractDLLs()
{
try
{
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.
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.
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.