Writing data to an XML file in Visual Basic 9 (Part 5) - Sorting a list

Alex asked the following question in the Channel 8 forums:

[...] cheers for that, I fixed up the code and gave my save button the DialogResult of OK so it works as it should except that it doesn’t organize the new waterpoints into alphabetical order until the program is restarted. The old version used to do this, how can I make it change order on the fly?

Luckily we have already the list of users in an instance of the List(Of T). This class has a method (Sort) that allows us to sort items, by using a Comparison delegate (more on delegates here) or an instance of a class that implements the IComparer interface.

Now we don’t want to have this method executed everywhere in the UI. Rather we want it to be encaspulated in a class (following part 4 where we separated the UI from the logic). I have therefore created a new class called UserList that inherits from List(of T) and added a method that sorts by the first name. That method invokes the original Sort method on the List(Of T) and returns after having done the sorting:

Imports System.Collections.Generic

‘ holds a list of user.
Public Class UserList
    Inherits List(Of User)

    ‘ sorts the list of items by first name.
    Public Sub SortByFirstName()

        ‘ sort by using our sorter method.
        Me.Sort(AddressOf FirstNameSorter)

    End Sub

    Private Function FirstNameSorter(ByVal x As User, ByVal y As User) As Integer

        ‘ if the first’s element first name is null return -1
        If x.FirstName Is Nothing Then
            Return -1
        End If
        ‘ if the second’s element first name is null return 1
        If y.FirstName Is Nothing Then
            Return 1
        End If

        ‘ otherwise use the build in comparison.
        Return x.FirstName.CompareTo(y.FirstName)

    End Function

End Class

The next step was to replace all the List(Of T) occurences with the UserList where it was required. After that I changed also the code to call the SortByFirstName method each time after an item has been added or edited.

Dim user = New User()

‘ show the edit for as dialog with this form as parent.
Using dialog = New EditXML(user)
    ‘ check if the dialog has been returned with a click on the OK button.
    If dialog.ShowDialog(Me) <> Windows.Forms.DialogResult.OK Then
        Return
    End If
End Using

‘ the dialog has been "OKed"; let’s add the user to our list.
_users.Add(user)

‘ sort the list.
_users.SortByFirstName()

UpdateControls(True)

The full source code can be downloaded form here.

Published on Mar 5th, 2008 — Tags: , , ,
Comments (0)    digg it!    kick it   

Writing data to an XML file in Visual Basic 9 (Part 4) - Separating the logic from the UI

The discussion, over at Channel 8, is getting more interesting. Alex is now asking me how to separate the UI from the business logic:

Now I’m just working on customizing the interface and this guy called Eric Seib said I should remove what code I can from the interface into seperate code files so I will start on that. I tried before but I kept having issues because as you know most of the code accesses text boxes on the form so it has to be there :| what do you think? Is it worth removing what code I can from the interface?

I haven’t done this in the previous posts because these are just samples. I didn’t separate the logic from the UI and add much error handling (and other stuff) for the sake to keep the code simple and easy to understand.

Now, for this blog post and to answer the question from Channel 8 I have decoupled the logic from the UI. Everything that is application logic has been moved to so called business objects. I have also tried to follow some common guidelines and therefore tried to stay a little bit conform with design patterns.

This is what I have done; in the form of a class diagram:

I have created a new class called “User”. I didn’t come up with a better name because I don’t know what Alex’ application is going to do: so I named it User. As you can see this class has some methods to load, save and delete itself. All the logic that’s relevant to the class is found in the class itself. It exposes also all the relevant information, such as FirstName, LastName, Location, etc., via properties.

To make it more convenient to load a list of “users” I have implemented the “UserLoader”. This class has just one method: LoadAll. The method loads all User objects that are found in the data path.

And last but not least the Settings class holds the data path information. It holds a property that returns the path to the xml files (the user xml files).

An updated version of the project can be downloaded from here.

Published on Mar 1st, 2008 — Tags: , , ,
Comments (4)    digg it!    kick it   

Writing data to an XML file in Visual Basic 9 (Part 3)

Over at Channel 8, Alex Mills asked another question:

As you know my program is built and expanded from the code you wrote, very much appreciated. You designed that when you click edit it opens the XML in a new window. Is it possible that when the listview selected index changes, it automatically loads a couple of nodes of the xml to text boxes on the main form. With one click to be used as a quick reference feature, but I still want to have the seperate editor as I dont want have all the text boxes on the main form. Is this possible?

Everything is possible :) As far as I have understood he needs something that looks like this:

To make this possible we first need to put some controls on the form. :) Next, we need to register (actually, we registered it already in the second example, but I didn’t mentioned it explicitely) the SelectedIndexChanged event of the ListView.

This event is always fired when the ListView’s selected item has changed. That applies also if the selection has been changed to no item being selected anymore. That comes in very handy because we want to clear the textboxes when no item has been selected and fill the textboxes when an item has been selected:

Private Sub ListView1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ListView1.SelectedIndexChanged

    ‘ enable or disable the buttons whether an item has been selected.
    Button1.Enabled = (ListView1.SelectedItems.Count > 0)
    Button3.Enabled = (ListView1.SelectedItems.Count > 0)

    ‘ check if an item has been selected
    If ListView1.SelectedItems.Count > 0 Then

        ‘ get the item from the list view.
        Dim path = CType(ListView1.SelectedItems.Item(0).Tag, FileInfo)

        ‘ load the preview data.
        LoadPreview(path.FullName)
    Else

        ‘ clear the preview text boxes.
        ClearPreview()

    End If

End Sub

The first two lines in the method are already here from the second sample. They only enable or disable the “Edit” and “Delete” buttons depending on whether an item has been selected or not. The if statement then checks whether an item has been selected. If one has been selected the selected item’s tag is read (remember, from sample two, the tag contains our file information object) and feed into a new method that is called LoadPreview. LoadPreview is for the most part only a copy of the code that is used in the constructor of the edit form and looks like this:

Private Sub LoadPreview(ByVal path As String)

    ‘ make sure that a path has been given
    If path = Nothing Then
        Throw New ArgumentNullException(“path”)
    End If

    ‘ load the xml document from the given path
    Dim xml = New XmlDocument()
    xml.Load(path)

    ‘ get the different nodes from the xml file and put them in the text boxes.
    ‘ ATTENTION: here should also added a check for the values being Nothing.
    Dim firstName = xml.SelectSingleNode(“//firstname”)
    TextBox1.Text = firstName.InnerText

    Dim lastName = xml.SelectSingleNode(“//lastname”)
    TextBox2.Text = lastName.InnerText

    Dim location = xml.SelectSingleNode(“//location”)
    TextBox3.Text = location.InnerText

End Sub

We get the path as argument of the method and load the XML file. After that we use some XPath to get the values in the XML file and put them in the different textboxes.

The ClearPreview is very simple and looks like this:

Private Sub ClearPreview()

    ‘ set all text boxes to an empty string.
    TextBox1.Text = String.Empty
    TextBox2.Text = String.Empty
    TextBox3.Text = String.Empty

End Sub

The full source code of the sample can be downloaded from here.

Published on Feb 28th, 2008 — Tags: , , ,
Comments (3)    digg it!    kick it   

Writing data to an XML file in Visual Basic 9 (Part 2)

Yesterday I have posted a blog post to answer a question that has been asked in the Channel 8 forums. The question was: “How can I save some values in text fields (on a form) as XML to the disk?”. After having written the blog post the guy asked me if he could also ask me further questions on the topic and I agreed.

So the next questions that came up in the forums are the following (summed up because the post is to long to be quoted):

  • How can I write an XML file to the Windows’ User folder? How do I get this folder?
  • How can I create an own folder with .NET?
  • How can I load the XML file again?
  • How can I show a list with all the files the folder holding my XML files?

Getting the Windows’ User folder is rather easy because the .NET Framework has a class called Environment. The class has a method called GetFolderPath that returns the locations of so called “special folders”. The Windows’ User folder is such a special folder.

Writing the XML file to that given path looks like this:

‘ create the file name. use the GetFolderPath to get the folder of my documents.
Dim path = New DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) & “\Waterpoints\”)
‘ if the path doesn’t exist create it.
If Not path.Exists Then
    path.Create()
End If

‘ create the file name from the path and the text box value.
Dim fileName = System.IO.Path.Combine(path.ToString(), firstNameTextBox.Text & “.xml”)

‘ compile the xml.
Dim xml = <?xml version=“1.0″ encoding=“utf-8″?>
          <userrecord>
              <firstname><%= firstNameTextBox.Text %></firstname>
              <lastname><%= lastNameTextBox.Text %></lastname>
              <location><%= locationTextBox.Text %></location>
          </userrecord>

‘ write the xml to the file.
xml.Save(fileName)

This code snippet addresses also the second question. There is a line in the code that checks whether the path already exists. If that’s not the case the Create method on the path is executed to create the directory. .NET makes creating paths really easy :)

The next question was on how to load an XML file. The .NET BCL (Base Class Library) offers a class that’s called XmlDocument. That class allows loading XML files and to search within the loaded files. XmlDocument creates a so called DOM while loading the file. A DOM is a structure that allows navigating and searching.

‘ load the xml document from the given path
Dim xml = New XmlDocument()
xml.Load(path)

‘ get the different nodes from the xml file and put them in the text boxes.
‘ ATTENTION: here should also added a check for the values being Nothing.
Dim firstName = xml.SelectSingleNode(“//firstname”)
firstNameTextBox.Text = firstName.InnerText

Dim lastName = xml.SelectSingleNode(“//lastname”)
lastNameTextBox.Text = lastName.InnerText

Dim location = xml.SelectSingleNode(“//location”)
locationTextBox.Text = location.InnerText

For the search part I’m using XPath. XPath is a handy query languages for XML files that searches within the DOM. The code doesn’t do any error handling. In the case of the XPath queries you sould always check if a result is returned. If the query doesn’t find an item this results in returning an object that is Nothing.

The last question was on how to show a list of XML files in a list view. For that I have created a new form: the MainForm. This form holds only a ListView and 3 buttons: one to create a new XML file, one to edit and one to delete an XML file.

The code to load the list looks like this:

Private Sub LoadFiles()

    ‘ remove all items in the list view.
    ListView1.Items.Clear()

    ‘ get the directory. if it does exist get the files from there.
    Dim directory = New DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) & “\Waterpoints”)
    If directory.Exists Then

        ‘ get all xml files in the given directory.
        Dim files = directory.GetFiles(“*.xml”)

        ‘ loop over the files and add them to the list view.
        For Each file In files
            Dim listItem = New ListViewItem(Path.GetFileName(file.FullName))
            listItem.SubItems.Add(file.FullName)
            listItem.Tag = file

            ListView1.Items.Add(listItem)
        Next

    End If
End Sub

This code is a little bit more complex. The complex part is how to fill the ListView with data. The ListView expects a ListViewItem. I set the Text property on that item (via the constructor) to the name of the file (without directories; the Path class offers useful methods to extract different parts of a path) and the first sub item to the FullName of the file (the FullName includes also the directories).

I have also set the file as “tag” of the ListViewItem. That’s handy because later when one of the items is selected I can retrieve the original file object from the tag. You can see the tag as a pocket where you can put one additional object in.

savetoxml2.png

I have also added more code (found in the sample sources) that enables and disabled the edit and delete button depending on whether an item has been selected in the list view.

The full source code of the sample can be downloaded here.

Side note: The example contains no error handling. I haven’t added that because the code gets a lot longer when doing error handling. Error handling should be added to the final application to make it more robust. For an introduction on how to do error handling in VB.NET click here.

Published on Feb 24th, 2008 — Tags: , , ,
Comments (10)    digg it!    kick it   

Writing data to an XML file in Visual Basic 9

Alex Mills asked the following question on Channel 8:

I’m building a application that via the use of a windows form, creates XML files that are also visible and editable by this application. What I dont know is how to set the contents of the texts fields to XML. Really what I need is to develop code that when I press save, saves all the text in the text fields into one XML file.

The first thing that came into my mind are the new XML literals that are available with Visual Basic 9 and I pointed him to Sven’s excellent blog post about them.

After a while I thought I could write this short example for him and upload it somewhere. Nothing easier than doing this directly on my blog :) I have extended the example by a FileSaveDialog instance (added to the Form) that shows a nice “Save as…” dialog when it comes to save the file. The code looks like this:

Imports System.IO

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        ‘ if the user did not click on the OK button return. we can do nothing.
        If SaveFileDialog1.ShowDialog(Me) <> Windows.Forms.DialogResult.OK Then
            Return
        End If

        ‘ get the file name.
        Dim fileName = SaveFileDialog1.FileName

        ‘ compile the xml by using XML literals.
        Dim xml = <?xml version=“1.0″ encoding=“utf-8″?>
                  <userrecord>
                      <firstname><%= firstNameTextBox.Text %></firstname>
                      <lastname><%= lastNameTextBox.Text %></lastname>
                      <location><%= locationTextBox.Text %></location>
                  </userrecord>

        ‘ write the xml to the file.
        xml.Save(fileName)

        ‘ show a message box with the results.
        MessageBox.Show(“The file has been successfully written to: “ + fileName, “Success!”)

    End Sub
End Class

A ZIP with the project can be downloaded from here.

I like XML literals because they make creating XMl files a lot easier. Sad that this feature is not part of C#. I really hope that C# will get something like this. The XDocument class (the VB9 compiler translates XML literals into XDocument calls) is nice but still doesn’t offer the easiness of XML literals. :)

Published on Feb 23rd, 2008 — Tags: , , , ,
Comments (21)    digg it!    kick it