Gigi Labs

Please follow Gigi Labs for the latest articles.

Saturday, November 9, 2013

C: Calculating the Average of Several Numbers

Hello everyone! :)

In my article "C: Hello World on Linux", we saw how to write a very simple C program, and how to compile and run it on Linux. In this article, we'll learn how to work with numbers in C, and also how to accept input and print output. I like to use Linux when writing or teaching C/C++, but you can just as well follow along if you're on Windows (e.g. by using Microsoft Visual C++). If you're going to use Linux, you can use your distribution's text editor, or else something like vi (check out my articles, "Linux: vi Essentials" and "Linux: Navigating Text with vi", if you feel you need a refresher on vi).

So, we'll begin with the following basic template:

#include <stdio.h>

int main(int argc, char ** argv)
{
    return 0;
}

Including the stdio.h header file gives us access to the input/output (I/O) functions that we'll need. The first of these is printf(), which we've already seen in my original article, "C: Hello World on Linux".

In our program, we're first going to ask the user how many numbers he's going to enter. Let's say he enters 5. In that case, we'll ask him for five numbers, and then add them up, and then compute the average.

Before the return statement, add the following:

    printf("How many numbers do you want to average?\n");

The \n translates to a newline.

We then declare a variable called count, of type int (integer), so that we can store how many numbers the user needs to enter:

    int count = 0;

Just a note... in C/C++ it is important to always initialise your variables with a value. If you don't, they will contain random garbage data, and using them before they're assigned might lead to unpredictable results.

Now, we can then use the scanf() function to accept input, as follows:

    scanf("%d", &count);

This looks pretty scary, doesn't it? The first parameter is a format string; it indicates what kind of data you're trying to accept. In this case, %d means we are expecting an integer. The second parameter indicates the variable where we'll store the input. By preceding the count variable with the & symbol, we're telling scanf() that the input should go to the location in memory where count resides. This might sound a bit weird until we talk about pointers and references (because it is), but just remember to include that & when accepting input, and you'll be fine.

Next, we need a few new variables:

    int i = 0;
    int number = 0;
    int total = 0;

We'll accept each number into the number variable, after which we'll add it to total. The i variable will be used to track how many numbers have been accepted, so that we know when to stop.

Next, we can write a for loop to repeatedly accept numbers as input, until we have reached the number of numbers we were expecting:

    for (i = 0; i < count; i++)
    {

    }

The code inside the curly brackets (which we will write shortly) will execute as many times as the count variable is set to. If count is 5, then it will execute 5 times. If you look at the first line of the for loop, we're starting with i set to zero, and we're telling it to continue executing as long as i is less than count. Each time the loop's code executes, i is increased by 1 (that's the i++ bit), until the i < count condition becomes true and the loop ends.

Sidenote: if you're coming from some other language, you might be wondering why I didn't declare i in the for loop. That's because some versions of C don't allow it (although it's fine in C++), so you may end up with an error like this:

error: ‘for’ loop initial declarations are only allowed in C99 mode

Let's add some code inside the for loop to accept numbers as input:

        printf("Enter number %d of %d: ", i, count);
        scanf("%d", &number);
        total = total + number;

In the first line, we can see the use of the %d (integer) placeholder within printf(). When you run the program, the placeholders are replaced with the other parameters, i.e. the first %d is replaced with the value of i, and the second one is replaced with the value of count.

In the second line, we're accepting a number as input, and storing it in the number variable (remember the &). Finally, in the third line, we add number to total. There's actually a more concise and efficient way of adding a value to a variable, which is the following:

        total += number;

After the end of the for loop, we can now calculate the average by dividing total by the number of items (count):

    float average = total / count;
    printf("Average: %f\n", average);

The float data type allows us to store numbers that are not integers (whole numbers), and which might have some decimal value. When using floats with printf(), we need to use the %f placeholder, instead of the %d that is used for integers.

Here's the code we have so far, with syntax highlighting courtesy of vim:


From the Linux terminal, the following command will compile your code to an executable called average:

gcc average.c -o average

...and the following command executes it:

./average

Let's test the program to see that it works correctly:


Hmmm, wait just a minute. 1 + 2 + 4 gives us 7, and divided by 3, that doesn't give us exactly 2 (it's actually 2.333333.....). The problem is in the following line of code:

    float average = total / count;

We're dividing an integer by another integer, and that gives us back an integer - losing any digits after the decimal point. To get this to work correctly, we can convert count to a float as follows:

    float average = total / (float) count;

This is called type casting: you put a data type in brackets before a variable to indicate that it should be interpreted as the specified data type. When we recompile and run the program, we can see that it now behaves as we originally expected:


Nice! :)

This article showed how to write a simple program - one that calculates the average of a set of numbers - in C. This simple program revealed several features of the C language, including input/output (via printf() and scanf()), placeholders for format strings (%d and %f), int and float variables, for loops, and type casting.

I hope you found this interesting. Stick around for more articles! :)

Wednesday, November 6, 2013

C#: XML Serialization

Hi guys and gals! :)

In this article, we're going to see how to do XML serialization, which is really just a fancy way of saying we want to load and save objects as files in XML format.

So you see, I've been a big fan of Warcraft 2 for a long time... probably about 15 years. In this game, you command different units (e.g. peasants, knights, etc), and each of them has different attributes such as Damage, Speed, etc. For instance, this is the mage:


Don't mess with that guy - he can turn you into a sheep! And here's the knight... not exactly a mild-mannered fellow, either:


If we represent those units and their attributes in XML, we might end up with something like this (I took out some of the extra crap that appears at the beginning when you actually save a file using XML serialization):

<UnitDatabase>
  <units>
    <Unit>
      <Name>Mage</Name>
      <Armor>0</Armor>
      <MinDamage>5</MinDamage>
      <MaxDamage>9</MaxDamage>
      <Range>2</Range>
      <Sight>9</Sight>
      <Speed>8</Speed>
    </Unit>
    <Unit>
      <Name>Knight</Name>
      <Armor>4</Armor>
      <MinDamage>2</MinDamage>
      <MaxDamage>12</MaxDamage>
      <Range>1</Range>
      <Sight>4</Sight>
      <Speed>13</Speed>
    </Unit>
  </units>
</UnitDatabase>

So, let's see how we can actually read and write a file like this in C#. Create a new Console Application in your favourite IDE.

We first need to create a class to represent our units with their attributes. Create a class and call it Unit. For convenience, we can implement the attributes as auto-implemented properties as follows:

        public String Name { get; set; }
        public int Armor { get; set; }
        public int MinDamage { get; set; }
        public int MaxDamage { get; set; }
        public int Range { get; set; }
        public int Sight { get; set; }
        public int Speed { get; set; }

This is just a quick alternative to declaring a member variable and a corresponding read-write property (available from .NET 3.0 onwards). For example, the Name property above is more or less equivalent to the following (just for demonstration - don't actually add it to your code):

        private String name;
       
        public String Name
        {
            get
            {
                return this.name;
            }
            set
            {
                this.name = value;
            }
        }

Next, add a constructor to set the attributes, so we can easily create Unit instances from our main program code:

        public Unit(String name, int armor, int minDamage, int maxDamage, int range, int sight, int speed)
        {
            this.Name = name;
            this.Armor = armor;
            this.MinDamage = minDamage;
            this.MaxDamage = maxDamage;
            this.Range = range;
            this.Sight = sight;
            this.Speed = speed;
        }

Now we need to create another class to hold an array of these units. Create a new class and call it UnitDatabase (admittedly a bit of a poor choice of a name, since it's not actually a database, but anyway). Give it a Units property as follows:

         public Unit[] Units { get; set; }

A constructor to assign this directly can also be pretty convenient. Add the following:

        public UnitDatabase(Unit[] units)
        {
            this.Units = units;
        }

Now we can implement our loading and saving code in UnitDatabase itself. Start by adding the code to save the UnitDatabase to a file:

        public void Save(String filename)
        {
            XmlSerializer ser = new XmlSerializer(typeof(UnitDatabase));
           
            using (StreamWriter sw = File.CreateText(filename))
                ser.Serialize(sw, this);
        }

You can see that we're making use of the XmlSerializer class. The file is saved by using its Serialize() method, which takes a TextWriter and the object to serialize. The StreamWriter returned by File CreateText() quite conveniently is a subclass of TextWriter, so we can pass it as the first parameter to Serialize(). The second parameter is this: the UnitDatabase itself.

To get this code to compile, you'll have to add the following using statements at the top:

using System.Xml.Serialization;
using System.IO;

Loading an XML file as a UnitDatabase is just as easy. In this case we make the method static since it isn't tied to any particular UnitDatabase instance:

        public static UnitDatabase Load(String filename)
        {
            XmlSerializer ser = new XmlSerializer(typeof(UnitDatabase));
           
            using (StreamReader sr = File.OpenText(filename))
                return ser.Deserialize(sr) as UnitDatabase;
        }

You can see that we're still using the XmlSerializer, but this time we use the Deserialize() method to read the file from disk and create a UnitDatabase from it. Deserialize() takes a TextReader, which again is a base class of the StreamReader that we get by calling File.OpenText(), so everything fits like magic. Deserialize() returns an Object, so as a last touch we cast this to a UnitDatabase using the as keyword. It's just the same as writing it like this:

                return (UnitDatabase) ser.Deserialize(sr);

That's all we need! Now, let's add some functionality to make it easy to write our units to the console output. All classes inherit from Object, and Object defines this ToString() method which we can use to return a string representation of our objects. This is very convenient in our case, so we can implement Unit's ToString() method as follows:

         public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("Name:      {0}", this.Name);
            sb.AppendLine();
            sb.AppendFormat("Armor:     {0}", this.Armor);
            sb.AppendLine();
            sb.AppendFormat("MinDamage: {0}", this.MinDamage);
            sb.AppendLine();
            sb.AppendFormat("MaxDamage: {0}", this.MaxDamage);
            sb.AppendLine();
            sb.AppendFormat("Range:     {0}", this.Range);
            sb.AppendLine();
            sb.AppendFormat("Sight:     {0}", this.Sight);
            sb.AppendLine();
            sb.AppendFormat("Speed:     {0}", this.Speed);
            sb.AppendLine();
           
            return sb.ToString();
        }

Note the override keyword in the method's signature. This means that we are replacing ToString()'s default functionality (which usually just returns the name of the class) with our own, in this case showing the unit's name and attributes.

Let's do the same for UnitDatabase. In this case we return a concatenation of all the units' string representations:

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            foreach (Unit unit in this.Units)
                sb.AppendLine(unit.ToString());
            return sb.ToString();
        }

To compile this code, you'll need to add the following line at the top of both files (because of the StringBuilder):

using System.Text;

Now all we have left to do is write code in Main() that actually uses these classes. We can start by creating our two units:

            Unit mage = new Unit("Mage", 0, 5, 9, 2, 9, 8);
            Unit knight = new Unit("Knight", 4, 2, 12, 1, 4, 13);

We can then combine these into an array using collection initializer syntax (see "C# Basics: Morse Code Converter Using Dictionaries" if you forgot what that is):

            Unit[] units = new Unit[] { mage, knight };

Then, we create a UnitDatabase out of this array:

            UnitDatabase db = new UnitDatabase(units);

...and finally save it to a file called units.xml:

            db.Save("units.xml");

You can now press F5 to run the program and see that it works. If you're using Visual Studio, you might have run into this error:


That's because XML serialization needs classes to have an empty constructor. SharpDevelop creates one for you when you create a new class, but Visual Studio does not. So if you're missing those, add them in. One for Unit:

        public Unit()
        {
           
        }

...and one for UnitDatabase:

        public UnitDatabase()
        {
           
        }

Good. Now press F5 to run the program, and then go to the project's bin\Debug folder to check that the units.xml file has been created. When you open it, it should look like this:

<?xml version="1.0" encoding="utf-8"?>
<UnitDatabase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Units>
    <Unit>
      <Name>Mage</Name>
      <Armor>0</Armor>
      <MinDamage>5</MinDamage>
      <MaxDamage>9</MaxDamage>
      <Range>2</Range>
      <Sight>9</Sight>
      <Speed>8</Speed>
    </Unit>
    <Unit>
      <Name>Knight</Name>
      <Armor>4</Armor>
      <MinDamage>2</MinDamage>
      <MaxDamage>12</MaxDamage>
      <Range>1</Range>
      <Sight>4</Sight>
      <Speed>13</Speed>
    </Unit>
  </Units>
</UnitDatabase>

It's got some more stuff in the first two lines that I showed you at the beginning, but that's just added there to make it a valid XML document and you can just ignore it.

At the end of Main(), let us now add code to load the file and display the unit data:

            UnitDatabase loadedDb = UnitDatabase.Load("units.xml");
            Console.WriteLine(loadedDb.ToString());
            Console.ReadLine();

Press F5 to see the result:


If you omit the ToString() as follows:

             Console.WriteLine(loadedDb);

...then the program works all the same, because Console.WriteLine() uses the ToString() method of the objects it is meant to write.

Great! :) In this article, we have seen how we can very easily save (serialize) objects as XML files, and load (deserialize) them back from XML files. To do this we need classes that match the XML structure, as well as the handy XmlSerializer class. Classes to be serialized must have a parameterless constructor. It is possible to do a lot more with XML serialization - there are several attributes that allow you to control the actual XML nodes and attributes that are written to the file.

We have also seen other aspects of C#, such as the ToString() method available in every object; how to override inherited methods; the as keyword which is an elegant alias for type casting; and auto-implemented properties.

Thanks for reading, and come visit Programmer's Ranch again in future! :)

Wednesday, October 30, 2013

C#: Unit Testing for Old or Express Visual Studio Editions Using NUnit

Hello folks! :)

I've written a few articles about unit testing so far. If you can use NUnit from within SharpDevelop, or use MSTest directly from within Visual Studio 2010+, then you are probably writing and running unit tests pretty comfortably.

Unfortunately, however, such luxury is not always available. Express editions of Visual Studio do not have the inbuilt MSTest features that the paid editions have; and older editions of Visual Studio did not have any unit testing functionality whatsoever. Fortunately, however, NUnit provides tools that can run unit test projects anyway. To show how this works, I'm going to demonstrate how to run unit tests for a project created in Visual Studio 2005.

The first thing we need is to download NUnit and then install it.


As we've already seen in "C#: Unit Testing with SharpDevelop and NUnit", this gives us the nunit.framework.dll file which we will need to reference in order to create our test project. You can find it in the bin\framework folder relative to wherever you installed NUnit:


Next, let's create our project via the File menu -> New -> Project... and then select Console Application and decide what to name your project and where to place it:


Make the Program class public so that our test project will be able to see it:

    public class Program

Then add the following method which we will be testing:

        public static String Shorten(String input)
        {
            if (input.Length > 20)
                return input.Substring(0, 18) + "...";
            else
                return input;
        }

This method shortens a piece of text that is longer than a specified length (in this case 20 characters) - it truncates the string and adds "..." to show that there's more to it. This is sometimes useful when you need to fit text in a fixed width, such as on an internet forum.

Now, let's write some tests. The first thing we need to do is create our test project. And since we don't have the luxury of Unit Test Projects like we saw towards the end of "Unit Tests, The Visual Studio Way", we'll make a Class Library instead. A class library is a project that results in a .dll file that you can deploy with your executable - it's great for splitting up more complex projects. You can put your classes and other stuff in a .dll, and other projects can use them as long as they're public (that's why we made the Program class public).

Right click on the solution and go Add -> New Project... . Select "Class Library" as the project type, name it CsNUnitExternal.Tests, and hit OK.

Right click the new project, select "Add Reference..." (which might seemingly hang older versions of Visual Studio, but just be patient until it becomes responsive again), select the Browse tab, and locate the bin\framework\nunit.framework.dll file from your NUnit installation.

Add another reference, this time via the Projects tab, to the original project where your code is.

Delete the Class1.cs file that's created by default, and add a new class instead (right click on the new project, Add -> Class...), called ProgramTests. Near the top, add the following line so that we can use NUnit's functionality:

using NUnit.Framework;

Just beneath it, add the following, so that we can use the Program class and its Shorten method from our original project:

using CsNUnitExternal;

Add the TestFixture attribute on top of the class name so that NUnit can recognise it:

    [TestFixture]
    class ProgramTests

Finally, add a couple of tests as follows:

    [TestFixture]
    class ProgramTests
    {
        [Test]
        void Shorten_LongString_Shortened()
        {
            String input = "In the jungle the mighty jungle the lion sleeps tonight";
            String expected = "In the jungle the ...";
            String actual = Program.Shorten(input);
            Assert.AreEqual(expected, actual);
        }

        [Test]
        void Shorten_ShortString_Shortened()
        {
            String input = "Midnight Special";
            String expected = "Midnight Special";
            String actual = Program.Shorten(input);
            Assert.AreEqual(expected, actual);
        }

This is what it looks like with colour and all - pretty enough to be placed inside a museum:


OK, now how do we run these tests? Conveniently, NUnit has this unit test runner program that we can use. You can get to it either from the bin folder in the NUnit installation, or via the Start menu (or whatever your Windows edition has instead).


Build your projects by pressing either F6 or Ctrl+Shift+B. Then, find your CsNUnitExternal.dll file in the relevant project's bin\Debug folder, and drag it onto the left part of the NUnit window (or load it via the File menu). The test class and relevant tests are loaded:


Running the tests now (Run button) will cause them to fail, and NUnit shows a big red bar as well as the reasons why the tests failed:


As the error messages indicate, we need to change our test class and methods to public, build the project again (at which point NUnit automatically reloads them), and then rerun the tests. We've gone green! :)

Sweet. In this article, we saw how we can use NUnit to run unit tests even when we have no in-built support for them from the IDE, such as old versions of Visual Studio (2008 and earlier), or Express editions of Visual Studio. We can do this by creating our tests in a class library, and then loading and running the tests in NUnit's own test runner, which is an external tool.

I hope you found this retro experience useful. Stay tuned for more articles coming soon! :)

Monday, October 28, 2013

Unit Tests, The Visual Studio Way

Hi! :)

Last month I wrote a couple of articles on unit testing: "C#: Unit Testing with SharpDevelop and NUnit" and "C#: Mocking and Dependency Injection for Unit Testing a File Sorting Program". These articles gave an introduction to unit testing using SharpDevelop and NUnit. If you're a Visual Studio user, however, you might prefer to use Visual Studio's own unit testing framework, known as MSTest. Visual Studio makes it really easy to run and debug tests directly from within the IDE, however writing the tests is slightly different.

In order to follow along, you'll need a version of Visual Studio from 2010 onwards, and one that includes unit testing (that doesn't include the Express editions, sorry). To demonstrate how to to write unit tests in Visual Studio, we're going to write a little program that tells us whether a year is a leap year or not.

First, let's create a new Console Application in Visual Studio 2010. You can do this by clicking on the "New Project..." link on the Start Page, or through the File Menu.


Above your Main() method, add a public static method called IsLeapYear():

        public static bool IsLeapYear(int year)
        {
            return false; // TODO
        }

We can now implement the logic that will decide whether the year is a leap year or not. We can use the leap year algorithm on Wikipedia. The leap year calculation, although not quite complex, might seem oddly contrived if you're looking at it for the first time. If you're curious about why there are so many conditions, you might want to check out this question on StackOverflow.

So the method ends up looking like this:

        public static bool IsLeapYear(int year)
        {
            if (year % 400 == 0)
                return true;
            else if (year % 100 == 0)
                return false;
            else if (year % 4 == 0)
                return true;
            else
                return false;
        }

The % is called a modulus operator - it performs a division but returns the remainder, not the result. So you know that year is divisible by 4 because the remainder is zero.

Right, now let's create some unit tests for our shiny new method. Before you run off and start belching out code, Visual Studio has this really neat feature where you can just right click somewhere empty and select "Create Unit Tests...":



In the screen that pops up, you can select the methods and properties for which you want to create unit tests. In our case it's just one method, but this is really useful if you have lots of code and want to generate a bunch of skeleton unit tests quickly.


In the "Output project" option at the bottom, you can choose whether to create a new test project or use an existing one. Again, in our case we can just create a new one, but if you already have a test project and want to add to it, you can just select it from the drop-down list. Anyway, hit "OK", name your unit tests project, and hit "Create". By convention, tests projects are usually named as [ProjectName].Tests .


Now, when you're done, you might get this message about InternalsVisibleTo:


See, this is because our Program class is not public, and that makes it internal by default. This means that the test project can't see it. Visual Studio allows projects to access non-public classes anyway by using this thing called InternalsVisibleTo, but in most cases I wouldn't recommend it. Just hit "No".

So now Visual Studio created a new project and some code for us:


In the test project, you'll notice that there's a reference to our LeapYearVs2010 project (because the tests need to use our actual code), and there's also a reference to this Microsoft.VisualStudio.QualityTools.UnitTestFramework thing (try fitting that in a business card) - that's needed for unit tests projects.

You'll also notice a bunch of code was generated. It's mostly full of crap, so let's just take out the bull and leave the beef:

using LeapYearVs2010;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

namespace LeapYearVs2010.Tests
{
    [TestClass()]
    public class ProgramTest
    {
        [TestMethod()]
        [DeploymentItem("LeapYearVs2010.exe")]
        public void IsLeapYearTest()
        {
            int year = 0; // TODO: Initialize to an appropriate value
            bool expected = false; // TODO: Initialize to an appropriate value
            bool actual;
            actual = Program_Accessor.IsLeapYear(year);
            Assert.AreEqual(expected, actual);
            Assert.Inconclusive("Verify the correctness of this test method.");
        }
    }
}

So, our usings include our LeapYearVs2010 project, and also a namespace for MSTest unit testing. Other than that, it's pretty straightforward: you have a test class (aptly marked with the [TestClass()] attribute, and it contains one or more unit tests (marked with the [TestMethod()] attribute). I know we haven't quite talked about attributes so far, but just stick them in the right places and don't worry about them.

Before we can write any proper unit test code, we'll need to make our Program class (in the LeapYearVs2010 project) public:

    public class Program

We can now write our first unit test. At times you might find the autogenerated skeleton code for unit tests useful, but in our case you can just throw it out and replace it with this:

        [TestMethod()]
        public void IsLeapYearTest()
        {
            int year = 2001;
            bool expected = false;
            bool actual = Program.IsLeapYear(year);
            Assert.AreEqual(expected, actual, "2001 shouldn't be a leap year!");
        }


You can now run the unit tests via the Test menu, -> Run -> All Tests in Solution, or else by right clicking on a TestClass or TestMethod and selecting "Run Tests". This right click option is context sensitive, so if you Run Tests on a TestMethod it will run that unit test; if you Run Tests on a TestClass it will run all unit tests for that class.

Right, now at this point, if you also have Visual Studio 2012 installed, you might run into this bug where the tests will just kinda get stuck, like this:


Apparently this is a known issue and you'll need to install Service Pack 1 for Visual Studio 2010 to get things working. When they do, you'll see a nice green tick mark next to your test:


Let us now add another test:

        [TestMethod()]
        public void IsLeapYearTest2()
        {
            int year = 1900;
            bool expected = true;
            bool actual = Program.IsLeapYear(year);
            Assert.AreEqual(expected, actual);
        }

Run it as before, and...


Whoops! Our second test failed. You can right click the test in the Test Results window and select "View Test Results Details" to obtain additional information including a stack trace (see screenshot above). If you need to, you can put a breakpoint in your test and then from the Test menu go to Debug -> Tests in Current Context while your cursor is in the unit test you want to debug. You can then debug the test as you would with normal code.

In our case, it's pretty clear that 1900 can't be a leap year because it's divisible by 100 but not by 400. So we need only update the expected value as follows:

            bool expected = false;

Finally, let's take a look at how to create unit tests in Visual Studio 2012 and 2013. I'm going to create a new Console Application in this dark-themed VS2013 which looks like it came out of a chimney:


I'm going to set up Program just as I did in VS2010: make the class public, and add the method as before:

        public static bool IsLeapYear(int year)
        {
            if (year % 400 == 0)
                return true;
            else if (year % 100 == 0)
                return false;
            else if (year % 4 == 0)
                return true;
            else
                return false;
        }

Now, let's create the unit tests. Right click in some empty space and...


Uhhhh... crap. Apparently the "Create Unit Tests..." option was removed in Visual Studio 2012, much to people's frustration. Fortunately, there are workarounds that can bring it back. If you don't want to do those, however, you're just going to have to create the test project and unit tests manually. To do this, right click on the solution, then Add -> New Project..., and from the Test category on the left, select "Unit Test Project". Give it a name and hit "OK".


This generates empty test code, and you can take off writing your unit tests from here. With VS2012, it's the same procedure.


Wow, this was long. Well, I hope it helped you learn about some of the unit testing tools that Visual Studio has to offer. It's very convenient because you can run and debug tests directly from within Visual Studio, and in Visual Studio 2010 there is the "Create Unit Tests" option which can be used to quickly generate skeleton code for unit tests of existing code. Sadly, this option is no longer there by default as from Visual Studio 2012, but there are ways of getting it back.

Check back for more programming goodness at Programmer's Ranch! :)

Thursday, October 17, 2013

C# WPF: Master/Detail Layout using MVVM

Welcome! :)

My recent article "C# WPF: Control Panel using MVVM" showed how you can make a specific master/detail layout suited for control panels - you select an option from the sidebar, and the interface on the right changes based on it. As I mentioned in the article, this approach is okay when the options are few and predefined. If you're working with displaying actual data, though, you need to work a little differently.

This is another advanced article dealing with MVVM, so prerequisites to understand the content include knowing C#, WPF, data binding, and a basic idea of MVVM. You will also need to download and install MVVM Light - a popular, lightweight MVVM library which we also used in yesterday's article to get some common MVVM functionality out of the box.

Let's start off by creating a new WPF application in SharpDevelop (or Visual Studio, if you prefer). Once that is done, right click on the project and select "Add Reference". From the ".NET Assembly Browser" tab, use the "Browse..." button to locate the "Mvvm Light Toolkit\Binaries\WPF4GalaSoft.MvvmLight.WPF4.dll" file from your MVVM Light installation. These instructions pertain to SharpDevelop using .NET 4, so if you're on Visual Studio or using a different version of the .NET Framework, adapt accordingly.

Next, let's set up a little master/detail layout to our Window that we will later populate. Replace the default <Grid> section with the following:

     <DockPanel>
        <ListBox DockPanel.Dock="Left" Width="100" />
        <Grid Height="100">
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="80" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBlock Text="Name:" Grid.Row="0" Grid.Column="0" />
            <TextBlock Text="Surname:" Grid.Row="1" Grid.Column="0" />
            <TextBlock Text="Age:" Grid.Row="2" Grid.Column="0" />
            <TextBlock Text="Location:" Grid.Row="3" Grid.Column="0" />
            <TextBox Grid.Row="0" Grid.Column="1" />
            <TextBox Grid.Row="1" Grid.Column="1" />
            <TextBox Grid.Row="2" Grid.Column="1" />
            <TextBox Grid.Row="3" Grid.Column="1" />
        </Grid>
    </DockPanel>

This gives us the following layout:


Not the prettiest thing, but that's beside the point. On the left, we have a ListBox where we'll display a list of names. On the right, we'll display the details for the selected person.

Next, let's create our ViewModel. Right click the project, Add -> New Item... and add a new class called MainVM.cs. In your Window's codebehind (Window1.xaml.cs in SharpDevelop, or MainWindow.xaml.cs in Visual Studio), set up this ViewModel as the DataContext by adding the following line at the end of its constructor:

             this.DataContext = new MainVM();

Add a new class called Person.cs. Set it up as follows:

     public class Person
    {
        public String Name { get; set; }
        public String Surname { get; set; }
        public int Age { get; set; }
        public String Location { get; set; }

        public String NameAndSurname
        {
            get
            {
                return String.Format("{0} {1}", this.Name, this.Surname);
            }
        }
      
        public Person(String name, String surname, int age, String location)
        {
            this.Name = name;
            this.Surname = surname;
            this.Age = age;
            this.Location = location;
        }
    }

This Person class represents the information we'll be displaying in our application. We can now go back to our ViewModel and begin to set up the properties that will actually be bound in the view (Window).

At the top of MainVM.cs, add the following:

using System.Collections.Generic;
using GalaSoft.MvvmLight;

Make MainVM inherit from MVVM Light's ViewModelBase:

    public class MainVM : ViewModelBase

We can now add member variables to represent our list of people and also the one that is currently selected:

        private List<Person> persons;
        private Person selectedPerson;

Add in the corresponding properties, and don't forget the RaisePropertyChanged() to let the data binding update the user interface when the properties change. RaisePropertyChanged() is something we get from MVVM Light by inheriting from ViewModelBase - see yesterday's article if you're feeling lost.

        public List<Person> Persons
        {
            get
            {
                return this.persons;
            }
            set
            {
                this.persons = value;
                RaisePropertyChanged("Persons");
            }
        }
      
        public Person SelectedPerson
        {
            get
            {
                return this.selectedPerson;
            }
            set
            {
                this.selectedPerson = value;
                RaisePropertyChanged("SelectedPerson");
            }
        }

In MainVM's constructor, let's add some data we can work with:

            this.persons = new List<Person>()
            {
                new Person("Chuck", "Norris", 21, "The Boogeyman's closet"),
                new Person("Monica", "Lewinski", 70, "Take a guess..."),
                new Person("Steve", "Ballmer", 55, "Crying somewhere"),
                new Person("Captain", "Jack", 30, "The Black Pearl")
            };

In the Window's XAML view, we can now add data binding to the ListBox as follows:

         <ListBox DockPanel.Dock="Left" Width="100" ItemsSource="{Binding Path=Persons}"
                 DisplayMemberPath="NameAndSurname" SelectedItem="{Binding Path=SelectedPerson}" />

So our ListBox is now getting its items from the Persons property we have just defined, and for each item (of type Person it displays the NameAndSurname property. We also bind the ListBox's SelectedItem property to the SelectedPerson property we just defined. We'll use that in a minute, but first, let's hit F5 to see that we're all good so far:


See, the names now appear in the ListBox on the left. We can now bind the fields on the right to properties of the SelectedPerson:

            <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=SelectedPerson.Name}" />
            <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=SelectedPerson.Surname}" />
            <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Path=SelectedPerson.Age}" />
            <TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Path=SelectedPerson.Location}" />

Once you run the application and select one of the names on the left, you'll see the details appear on the right (I resized the window a little so that things fit a little better):


Now, this approach might work if you can afford to load all your data in memory - i.e. you don't have a lot of items, and they don't contain a lot of data. When you're dealing with larger data sets, it is probably best to take the following approach instead:

  1. Bind the ListBox to a list of objects that contain minimal information necessary for the master view to work - in this case, the name, surname, and an ID corresponding to a primary key for that record in the database.
  2. Add a SelectedPersonDetails property, in which you keep full details for the selected record.
  3. In the SelectedPerson's set modifier, implement code to fill SelectedPersonDetails with details from the database based on the selected ID.
  4. Bind the detail fields to properties of SelectedPersonDetails.

In this article, we have seen a simple way of implementing a master/detail view using the MVVM design pattern. We did this by maintaining properties representing the master view's items and selected items, and binding to them. Fields in the detail view could then be bound to properties of the selected item. In case a lot of data is involved, it is recommended to populate the list with basic data and then retrieve details lazily (on demand) as needed.

Thanks for reading; come back again! :)