Update (5/10/2010)
New code available for C# 2010 Express (cleaned up version also). Download the 2010 version, if 2008 code gives you problem in 2010.
Introduction
This is my first article about my very first C# study application. While I was reading books and watching videos, I thought of creating something that I would also use, nothing fancy, simple to use. This application can be used for notes as well, not only for codes. The code itself, not the application and its functionality, is the most important part, and will be the main part of my following articles (if I am approved by you... the readers).
Background
I started this code using Access DB. Then I purchased a laptop that came with 64bit Win7 OS. This is my first 64 bit machine and I didn't know how my code would react. Apparently there are some issues with Microsoft Jet engine vs 64 bit systems. Even though I found out some fixes and fix-arounds, I decided to move on to other things. I tried SQL Express... very powerful database, I love SQL. I used SQL servers for my previous applications (not in C#), and I love it. Although, I wanted this program to be very light and very "portable + Sharable" without any pain (or less pain). So, instead of using a "Home-made" text based DB, I decided to use XML. This way, if someone wants to transfer XML into another database, they would be able to do so easily. This is my first time dealing with XML, and I enjoyed it a lot.
Using the Code
There are two forms and two custom classes for this program:
- Form1 (Main form)
- Form Settings
- IO class
- XML class
The application lets you create a new XML file with three fields (ID, Code subject and main code) for its database, or locate an existing one. So, if multiple users are using different databases, they can share their XML files, also one can be shared between applications throughout the office or home. I didn't put any restrictions, such as log-ins and/or user levels to make changes. Maybe some of you want to add an option to give the user the flexibility of creating as many fields they want for their own purposes as well... all those things can be added into this code. Sky is the limit. :)
Since the entire code is available for download, I would like to show a few methods/codes here that I like because of their work.
Tooltip setup: I use "_toolTip
" class in almost all my "form load" methods to show control tips for my users. Simple, but since this is a beginners' article, I thought maybe some of you might have just missed it. There is also "#region
" "#endregion
" usage here. It's a very nice tool to pack some of your code out of the way.
#region ToolTips
_toolTip.SetToolTip(btnCopy, "Copy to ClipBoard");
_toolTip.SetToolTip(btnNew, "Add new code with this subject");
_toolTip.SetToolTip(btnExit, "Exit program");
_toolTip.SetToolTip(lstSubjects, "Click to see the code");
_toolTip.SetToolTip(chkOnTop, "Check to keep this window top of all windows");
_toolTip.SetToolTip(btnSearch, "Search DB");
_toolTip.SetToolTip(txtCodeMain, "Double Click to copy code into clipboard");
#endregion
Resizing and positioning the form: Even there is "StartPosition
" property for Windows forms, I still use following code to adjust my form size using current screen bounds.
this.Width = Screen.PrimaryScreen.Bounds.Width / 2;
this.Height = Screen.PrimaryScreen.Bounds.Height - 300;
I use "Split
" a lot in my codes. Sometimes it is much easer to pass information throughout classes and methods. Split also a "cheap" way to pass field information, I believe it does take less memory than arrays. Finding the right character is the thing, which I used "backspace" character here, since it can't be passed through a textbox.
XMLDatabase clsXML = new XMLDatabase(); ArrayList CurrentSubjectList = new ArrayList(); string[] SplitText = new string[2];
CurrentSubjectList = clsXML.GetSubjectList
(clsIO.XMLPath, SearchWord); char Splitchr = (char)8;
foreach (string FullString in CurrentSubjectList) {
SplitText = FullString.Split(Splitchr); ListViewItem s_Items; s_Items = lstSubjects.Items.Add
(SplitText[0]); s_Items.SubItems.Add(SplitText[1]); }
ListView
control is one of my favorite controls (above). When used with "Details
" mode, it can be a very nice tool to show items for my database fields. I don't like bounding data, so everything is dynamically coded to fill ListView
control... even when I use SQL or Access database. No bounding... I hate bounding things to my application. The application must be totally free of ties between any data source, and connection should be done after program runs, when needed... (totally personal).
I probably won't repeat all this "simple" information for my next articles, but they're extremely useful things which some of you might be missing.
Here is another idea to change some of the control properties depending on a condition. It's a class that enables/disables controls during runtime.
private void EnableDisableButtons(bool EDButtons) {
TSAddNew.Enabled = EDButtons;
TSEdit.Enabled = EDButtons;
TSDelete.Enabled = EDButtons;
TSRefresh.Enabled = EDButtons;
btnSearch.Enabled = EDButtons;
btnNew.Enabled = EDButtons;
btnEdit.Enabled = EDButtons;
btnDelete.Enabled = EDButtons;
txtCodeMain.Enabled = EDButtons;
txtSubject.Enabled = EDButtons;
}
The following code shows "overload" class. I had to keep the original constructor and add another one, because I need to set some of the control values during instantiation.
public frmSettings()
{
InitializeComponent();
}
Overload:
public frmSettings(string OnTopStr, string WordWrapStr, string AutoSaveStr)
{
InitializeComponent();
if (OnTopStr == "1")
{ chkOnTop.Checked = true; }
if (WordWrapStr == "1")
{ chkWordWrap.Checked = true; }
if (AutoSaveStr == "1")
{ chkAutoSave.Checked = true; }
IOFile clsIO = new IOFile();
if (clsIO.ErrHap == null)
{
Scripting.FileSystemObject FsO =
new Scripting.FileSystemObject(); lblXML.Text = "XML File Name : " +
FsO.GetFileName(clsIO.XMLPath); XMLDatabaseLocation = clsIO.XMLPath; }
}
The rest of the article will show XML file manipulations. XML portion is probably the only place my code gets complicated(!). I hope you will find it useful.
Create an XML file with 3 child nodes. So, there is a main tag, a parent tag and three children tags.
XmlTextWriter XMLWrite = new XmlTextWriter(XMLPath, System.Text.Encoding.UTF8);
XMLWrite.WriteStartDocument();
XMLWrite.WriteStartElement("Codes"); XMLWrite.WriteStartElement("NewCode"); XMLWrite.WriteElementString("ID", "1"); XMLWrite.WriteElementString("DBSubject", "Welcome"); XMLWrite.WriteElementString("DBCode",
"Thank you for using CodeDatabase by Leo Koach (2010)"); XMLWrite.WriteEndElement();
XMLWrite.WriteEndElement();
XMLWrite.WriteEndDocument();
XMLWrite.Flush();
XMLWrite.Close();
Appending records (nodes) at the end of the XML file:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(XMLPath);
XmlElement subRoot = xmlDoc.CreateElement("NewCode");
XmlElement appendedElementID = xmlDoc.CreateElement("ID");
XmlText xmlTextID = xmlDoc.CreateTextNode(GetLatestID(XMLPath));
appendedElementID.AppendChild(xmlTextID);
subRoot.AppendChild(appendedElementID);
xmlDoc.DocumentElement.AppendChild(subRoot);
XmlElement appendedElementSubject = xmlDoc.CreateElement("DBSubject");
XmlText xmlTextSubject = xmlDoc.CreateTextNode(CodeSubject);
appendedElementSubject.AppendChild(xmlTextSubject);
subRoot.AppendChild(appendedElementSubject);
xmlDoc.DocumentElement.AppendChild(subRoot);
XmlElement appendedElementCode = xmlDoc.CreateElement("DBCode");
XmlText xmlTextCode = xmlDoc.CreateTextNode(CodeMain);
appendedElementCode.AppendChild(xmlTextCode);
subRoot.AppendChild(appendedElementCode);
xmlDoc.DocumentElement.AppendChild(subRoot);
xmlDoc.Save(XMLPath);
There might be another way to edit a node information, although this looks pretty good and works just fine for me. All I am doing here is to loop XML nodes and search for ID field for the ID I'm passing. If a match is found, change the selected field information with the new one.
XmlDocument XMLEdit = new XmlDocument();
XMLEdit.Load(XMLPath);
XmlElement XMLEditNode = XMLEdit.DocumentElement;
foreach (XmlNode node in XMLEditNode) {
if (node["ID"].InnerText == SubjectID) {
node["DBCode"].InnerText = CodeMain;
break;
}
}
XMLEdit.Save(XMLPath);
Deleting a portion of the XML file is done as follows. Just like the above code, I am looking for the ID field. If I find it, I only delete the parent node that ID field and other children fields located. I probably could merge those two loops into one, but for learning purposes, this is ok. You're always welcome to do your own merging. That could save you writing extra lines of code.
XmlDocument XMLDelete = new XmlDocument();
XMLDelete.Load(XMLPath);
XmlElement XMLDeleteNode = XMLDelete.DocumentElement;
foreach (XmlNode node in XMLDeleteNode)
{
if (node["ID"].InnerText == SubjectID) {
node.ParentNode.RemoveChild(node); break;
}
}
XMLDelete.Save(XMLPath);
Get a field information (node) from XML file, and throw an exception. I included a bit more code here to show how I used the Try
/Catch
and Exception thrower. When an exception is thrown like this, it comes back to the "sender" (I like to call it that way, it's actually sending the exception back to the method which class was initiated). Same as other three codes, I loop through the XML until I find my ID.
XmlDocument XMLRead = new XmlDocument();
try
{
XMLRead.Load(XMLPath);
XmlNodeList XMLItems = XMLRead.SelectNodes
("Codes/NewCode");
foreach (XmlNode node in XMLItems) {
if (node["ID"].InnerText == SubjectID) {
StrInf = node["DBCode"].InnerText; break; }
}
return StrInf;
}
catch (Exception exc)
{
throw exc;
}
}
By the way, the ID field always grows to higher numbers even if anything is deleted from the XML file. I could (and it is easy to do) find the missing number and assign that for the next ID (for example, I have data from ID number 1 through 40... and I deleted number 23. So there is a gap now. Next time when I enter a new data, I could just find number 23 and assign my ID to it). I like the way it is though. To get the latest number, I have another loop that reads the ID field only and adds "1
".
public string GetLatestID(string xmlFilePath)
{
int LastIDEntry = 0;
XmlDocument XMLGetLastID = new XmlDocument();
XMLGetLastID.Load(xmlFilePath);
XmlNodeList XMLItems = XMLGetLastID.SelectNodes("Codes/NewCode");
foreach (XmlNode node in XMLItems)
{
LastIDEntry = Convert.ToInt32(node["ID"].InnerText) + 1;
}
return Convert.ToString(LastIDEntry);
}
I know there are probably better ways than looping through XML files, which could slow things down when the file gets larger, and I need your help to find them (if any).
Points of Interest
This code is for C# beginners (like myself). I love coding and enjoy any challenges that come with it. I research a lot to learn (even at my age) a lot. These days, internet is my only source (99.90% of the time) to do research for C#. Hopefully some of you will copy/paste and use some of my code here. I like to modify the code I find to fit my code, which is probably the best way to learn (next to watching C# videos). I hope to see you at my next article "Study II". :)