Gigi Labs

Please follow Gigi Labs for the latest articles.

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 { getset; }
        public String Surname { getset; }
        public int Age { getset; }
        public String Location { getset; }

        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! :)

Wednesday, October 16, 2013

C# WPF: Filter ListBox As You Type, Using MVVM

Hellooooo! :)

In this article, I'm going to show how you can set things up so that typing in a WPF TextBox instantly filters the items in a ListBox, within an MVVM setting.

This is an advanced article, and to understand it, you'll need to know your C#, WPF and data binding as well as a little touch of MVVM and LINQ.

So, let's get to it. Create a new WPF Application in SharpDeveloper or whichever IDE you prefer.

In your Window's XAML view, replace the default Grid element with the following:

     <DockPanel>
        <TextBox DockPanel.Dock="Top" />
        <ListBox />
    </DockPanel>

Simple enough? The TextBox is where you type your text, and the ListBox will show a list of items.

Let us now create a ViewModel for our Window. Create a new class (right click on project, Add -> New Item...) and name it MainVM. At the beginning of the MainVM class, add the following member variables:

        private List<String> names;
        private String filter;

Right after that, add the following properties to expose them:

        public List<String> FilteredNames
        {
            get
            {
                return (from name in names where name.Contains(filter) select name).ToList<String>();
            }
        }

        public String Filter
        {
            get
            {
                return this.filter;
            }
            set
            {
                this.filter = value;
            }
        }

Notice how we aren't exposing our names variable as it is; we are returning only those names that contain the filter text (which will be set via the TextBox later). You might prefer to use StartsWith() instead of Contains() - that's up to you.

If you're using SharpDevelop, don't forget to add the following at the top for the Lists and the LINQ:

using System.Collections.Generic;
using System.Linq;

In the MainVM() constructor, let's initialise our variables:

            this.names = new List<string>() { "Jerry""Joey""Roger""Raymond""Jessica""Mario""Jonathan" };
            this.filter = "";

In your Window's codebehind (Window1.xaml.cs if you're in SharpDevelop, or MainWindow.xaml.cs if you're using Visual Studio), set up the viewmodel as the datacontext for the window by adding this line at the end of the constructor:

             this.DataContext = new MainVM();

Back in the window's XAML, we can now change our controls and add bindings in order to get our list to show:

        <TextBox DockPanel.Dock="Top" Text="{Binding Path=Filter}" />
        <ListBox ItemsSource="{Binding Path=FilteredNames, Mode=OneWay}" />

Here's the result:


Great, the list is showing. But as you can see, the list isn't being filtered by the text in the TextBox. To get this working, there are two things we need to do. First, we need to update the TextBox's binding to use an UpdateSourceTrigger as follows:

<TextBox DockPanel.Dock="Top" Text="{Binding Path=Filter, UpdateSourceTrigger=PropertyChanged}" />

This causes the underlying property to be updated with every keypress, rather than when the TextBox loses focus. This will give the idea that the list is being updated in real-time.

The second thing we need to do is actually let the WPF binding know that FilteredNames needs to be updated when the Filter property is set. WPF allows us to do this by implementing INotifyPropertyChanged. We can actually implement INotifyPropertyChanged ourselves, or use the functionality by means of an MVVM library. Let's see how both these methods work.

Method 1: Implement INotifyPropertyChanged

Add the following code at the end of your MainVM class:

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(String propertyName)
        {
            PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));
        }

Then make your MainVM class implement INotifyPropertyChanged:

     public class MainVM : INotifyPropertyChanged

Change your Filter property's set accessor as follows to let the binding know that it has to refresh the FilteredNames:

            set
            {
                this.filter = value;
                NotifyPropertyChanged("FilteredNames");
            }

Finally, add the following at the top so that the compiler knows what an INotifyPropertyChanged is:

using System.ComponentModel;

Hit F5 and try it out to see that it works:

Method 2: Use MVVM Light

There's this library called MVVM Light written by a dude called Laurent Bugnion that packages some stuff we normally need for MVVM. It's not comfortable to have to keep an event in all our viewmodels, so instead we can inherit from a common base class.

First, download and install MVVM Light to some local folder. Then, locate Mvvm Light Toolkit\Binaries\WPF4\GalaSoft.MvvmLight.WPF4.dll and add a reference to it (right click project, Add Reference, then select the .NET Assembly Browser tab and locate the .dll via the Browse... button.

At the top of your MainVM.cs file, put the following to allow you to make use of this library:

using GalaSoft.MvvmLight;

Next, let the MainVM class inherit from ViewModelBase, a common base class for viewmodels available in MVVM Light:

     public class MainVM : ViewModelBase

Now, you only need to update your Filter property's set accessor as follows in order to make things work:

            set
            {
                this.filter = value;
                RaisePropertyChanged("FilteredNames");
            }

RaisePropertyChanged() is a method inherited from ViewModelBase, and does pretty much the same thing as NotifyPropertyChanged() from Method 1 above.

Press F5 to give it a shot:


Summary

In this article we saw how easy it is to filter a ListBox as you type into a TextBox. We exploited data binding in an MVVM manner, and used INotifyPropertyChanged (either directly or through MVVM Light) to reflect changes in the ListBox.

This is not necessarily the most efficient way of doing this, but it's simple to implement and is pretty fast even if you have several hundred items in your ListBox.

I hope you found this useful, and check back for more articles! :)

Wednesday, October 9, 2013

C# WPF: Control Panel using MVVM

Hi all! :)

In this article I'm going to show how you can implement a simple control panel in WPF using the MVVM design pattern. A control panel is a special case of a master/detail layout, where options are usually few and predefined, so you don't need to load any detail dynamically as options are selected.

This is an advanced article, so read on only if you have a fair grasp of the following: C#, WPF, data binding, and MVVM. I'm going to be using Visual Studio 2013 RC for the examples (just to try it out, really), but you should be able to use SharpDevelop or any version of Visual Studio from 2008 onwards.

Before we begin, a brief note on MVVM is in order. MVVM (Model-View-ViewModel) is an approach that promotes using data binding and commands over events. What we're going to do in this article can easily be done using the event-driven model (a la Windows Forms), but I'm writing this mainly for those who are trying to adopt the MVVM approach and who (like me) had a hard time grasping how it works.

So let's start off by adding a new C# WPF Application:


The New Project window above is an example of a master/detail layout. As you click on items in the treeview on the left (e.g. Windows or Web), the list of items in the middle changes accordingly. The treeview is a master view, while the area in the middle is the detail view.

For starters, in the XAML view, we can do away with the default <Grid> and set up our (not yet functional) master/detail layout as follows:

    <DockPanel>
        <ListBox Width="150" DockPanel.Dock="Left" />
        <ContentControl />
    </DockPanel>

Here's a screenshot of what it looks like in Visual Studio:


Isn't that simple? We use a ListBox for the master view, and move it to the left thanks to the DockPanel that contains it. The detail view is a ContentControl, which is a container we can use to hold user controls.

Next, we need a ViewModel for our MainWindow. Add a new class (right click on project, select Add -> New Item...) and call it MainVM. Make the class public, and set up some hardcoded values for the options that will appear in the master view:

    public class MainVM
    {
        private List<String> operations;

        public List<String> Operations
        {
            get
            {
                return this.operations;
            }
        }

        public MainVM()
        {
            this.operations = new List<String>() { "Add User", "List Users" };
        }
    }

In MainWindow's codebehind (i.e. MainWindow.xaml.cs), set the DataContext to an instance of MainVM in the constructor. This is important because when we do our data binding, the properties are always relative to the DataContext.

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = new MainVM();
        }
    }

Back in our XAML view for MainWindow.xaml, we can now see that our binding works by adding an ItemsSource to the master ListBox:

<ListBox Width="150" DockPanel.Dock="Left" ItemsSource="{Binding Path=Operations}" />

Note how we're binding to the Operations property, and this is interpreted as belonging to the DataContext, which is a MainVM in this case. Sure enough, pressing F5 to debug the project shows that the ListBox is filled with the values we hardcoded earlier:


Right, now to actually fill in the detail view depending on what master option is selected. We'll first create each detail as a separate UserControl - this will allow us to easily plug them into our window.

Add your first UserControl (right click on project, Add -> New Item... and select "User Control (WPF)" - don't confuse it for the "User Control" which is actually a Windows Forms thing) and name it AddUserControl.xaml. Instead of implementing functionality to add or list users (which isn't the point of this article, we'll just stick some static content instead. Put this instead of the default grid:

    <StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock VerticalAlignment="Center">Name:</TextBlock>
            <TextBox Width="100" Margin="5 5 5 5" />
        </StackPanel>
    </StackPanel>

Create another UserControl, ListUsersControl.xaml, and replace the grid with the following:

    <StackPanel>
        <TextBlock>John Doe</TextBlock>
        <TextBlock>Mary Jane</TextBlock>
        <TextBlock>John Smith</TextBlock>
        <TextBlock>Chuck Norris</TextBlock>
    </StackPanel>

Now, we need to map each operation name (e.g. "Add User") to the corresponding UserControl. One option is to use a Tuple, but a better solution is to create a dedicated class. Add a new class named Operation and implement it like this:

    public class Operation
    {
        public String Name { get; set; }
        public UserControl Control { get; set; }

        public Operation(String name, UserControl control)
        {
            this.Name = name;
            this.Control = control;
        }
    }

You will also need to add the following near the top for UserControl to make sense:

using System.Windows.Controls;

Back in MainVM, replace the whole (String-based) class with the following (Operation-based):

    public class MainVM
    {
        private List<Operation> operations;

        public List<Operation> Operations
        {
            get
            {
                return this.operations;
            }
        }

        public MainVM()
        {
            this.operations = new List<Operation>();
            this.operations.Add(new Operation("Add User", new AddUserControl()));
            this.operations.Add(new Operation("List Users", new ListUsersControl()));
        }
    }

All we have left to do is wire up the bindings on the XAML view of MainWindow.xaml. First, we need to change our ListBox as follows:

        <ListBox Name="MasterView" Width="150" DockPanel.Dock="Left"
                 ItemsSource="{Binding Path=Operations}" DisplayMemberPath="Name" />

I've added two things here. First, I added a name to the ListBox so that I can refer to it from my ContentControl. I also added a DisplayMemberPath. This tells the ListBox that for each Operation item, it needs to show the Name property. If you leave that out, it will just display Operation.ToString() by default.

Next, we bind our ContentControl's Content property as follows:

        <ContentControl Content="{Binding ElementName=MasterView, Path=SelectedItem.Control}" />

The binding refers to the control named MasterView (i.e. our ListBox), and we hook it up with the ListBox's SelectedItem property. Since the SelectedItem is actually an Operation, we get the UserControl associated with that Operation (via the Control property). Setting the Path to SelectedItem.Control does the trick.

That's it! Press F5 to test the application. When you click on "Add User", you get the correct UserControl:


...and clicking on "List Users" works just as well:


Woohoo! :) We managed to implement an MVVM master/detail control panel with just three lines of XAML (two controls, really) and some data binding magic.

As you have seen, the particular nature of a control panel makes an MVVM implementation particularly easy, because each detail view can reside in memory. This does not apply to all master/detail layouts though: if the master list is really long, it may be a better idea to load detail views lazily on demand. That could be the topic for a future article. Stay tuned! :)

Sunday, October 6, 2013

C#: Extracting Zipped Files with SharpZipLib

Hello there! :)

In this article, we're going to learn how to extract files from a .zip file using a library called SharpZipLib.
Additionally, we will also learn how to work with third-party libraries with functionality we might not get by default. This is because SharpZipLib is not part of the .NET Framework, and is developed by an independent team.

To get started, we need to create a new Console Application in SharpDevelop (or Visual Studio, if you prefer), and also download SharpZipLib from its website. When you extract the contents of the .zip file you downloaded, you'll find different versions of ICSharpCode.SharpZipLib.dll for different .NET versions. That's the library that will allow us to work with .zip files, and we need the one in the net-20 folder (the SharpZipLib download page says that's the one for .NET 4.0, which this article is based on).

Now, to use ICSharpCode.SharpZipLib.dll, we need to add a reference to it from our project. We've done this before in my article "C#: Unit Testing with SharpDevelop and NUnit". You need to right click on the project and select "Add Reference":


In the window that comes up, select the tab called ".NET Assembly Browser", since we want to reference a third party .dll file. Open the ICSharpCode.SharpZipLib.dll file, click the "OK" button in the "Add Reference" dialog, and you're ready to use SharpZipLib.


In fact, you will now see it listed among the project's references in the Projects window (on the right in the screenshot below):


Great, now how do we use it?

This is where our old friend, Intellisense, comes in. You might recall how we used it to discover things you can do with strings in one of my earliest articles here, "C# Basics: Working with Strings". This applies equally well here: as you begin to type your using statement to import the functionality you need from the library, Intellisense suggests the namespace for you:


Now SharpZipLib has a lot of functionality that allows us to work with .zip files, .tar.gz files and more. In our case we just want to experiment with .zip files, so we're fine with the following:

using ICSharpCode.SharpZipLib.Zip;

That thing is called a namespace: it contains a set of classes. If you type it into your Main() method and type in a dot (.) after it, you'll get a list of classes in it:


A namespace is used to categorise a set of related classes so that they can't be confused with other classes with the same name. Java's Vector class (a kind of resizable array) is a typical example. If you create your own Vector class to represent a mathematical vector, then you might run into a naming conflict. However, since the Java Vector is actually in the java.util namespace, then its full name is actually java.util.Vector.

This works the same way in C#. The List class you've been using all along is called is actually called System.Collections.Generic.List. We usually don't want to have to write all that, which is why we put in a using statement at the top with the namespace.

When we're working with a new namespace, however, typing the full name and using Intellisense allows us to discover what that namespace contains, without the need to look at documentation. In the screenshot above, we can already guess that ZipFile is probably the class we need to work with .zip files.

Intellisense also helps us when working with methods, constructors and properties:


I suppose you get the idea by now. Let's finally actually get something working. To try this out, I'm going to create a zip file with the following structure:

+ test1.txt
+ folder
    + test2.txt
    + test3.txt

I've used WinRAR to create the zip file, but you can use anything you like. I named it "zipfile.zip" and put it in C:\ (you might need administrator privileges to put it there... otherwise put it wherever you like). Now, we can easily obtain a list of files and folders in the .zip file with the following code:

        public static void Main(string[] args)
        {
            using (ZipFile zipFile = new ZipFile(@"C:\\zipfile.zip"))
            {
                foreach (ZipEntry entry in zipFile)
                {
                    Console.WriteLine(entry.Name);
                }
            }
         
            Console.ReadLine();
        }

This gives us:


We use the using keyword to close the .zip file once we're done - something we've been doing since my article "C#: Working with Streams". You realise you need this whenever you see either a Dispose() or a Close() method in Intellisense. We are also using looping over the zipFile itself - you realise you can do a foreach when you see a GetEnumerator() method in Intellisense. Each iteration over the zipFile gives us a ZipEntry instance, which contains information about each item in the .zip file. As you can see in the output above, entries comprise not just files, but also folders.

Since we want to extract files, folders are of no interest for us. We can use the IsFile property to deal only with files:

                    if (entry.IsFile)
                        Console.WriteLine(entry.Name);

In order to extract the files, I'm going to change the code as follows:

        public static void Main(string[] args)
        {
            using (ZipFile zipFile = new ZipFile(@"C:\\zipfile.zip"))
            {
                foreach (ZipEntry entry in zipFile)
                {
                    if (entry.IsFile)
                    {
                        Console.WriteLine("Extracting {0}", entry.Name);
                 
                        Stream stream = zipFile.GetInputStream(entry);
                        using (StreamReader reader = new StreamReader(stream))
                        {
                            String filename = entry.Name;
                            if (filename.Contains("/"))
                                filename = Path.GetFileName(filename);
                             
                            using (StreamWriter writer = File.CreateText(filename))
                            {
                                writer.Write(reader.ReadToEnd());
                            }
                        }
                    }
                }
            }
         
            Console.ReadLine();
        }

Note that I also added the following to work with File and Path:

using System.IO;

Extracting files involves a bit of work with streams. The zipFile's GetInputStream() method gives you a stream for a particular entry (file in the .zip file), which you can then read with a StreamReader as if you're reading a normal file.

I added a bit of code to handle cases when files are in folders in the .zip file - I am finding them by looking for the "/" directory separator in the entry name, and then extracting only the filename using Path.GetFileName(). [In practice you might have files with the same name in different folders, so you'd need to actually recreate the folders and put the files in the right folders, but I'm trying to keep things simple here.]

Finally, we read the contents of the entry using reader.ReadToEnd(), and write it to an appropriately named text file. If you run this program and go to your project's bin\Debug folder in Windows Explorer, you should see the test1.txt, test2.txt and test3.txt files with their proper contents. [Again, the proper way to deal with streams is to read chunks into a buffer and then write the file from it, but I'm using reader.ReadToEnd() for the sake of simplicity.]

Excellent! In this article, we have learned to list and extract files from a .zip file. We also learned why namespaces are important. But most importantly, we have looked at how to reference third party .dlls and discover how to use them based only on hints from Intellisense and our own experience. In fact, the above code was written without consulting any documentation whatsoever, solely by observing the intellisense for SharpZipLib. While it is usually easier to just find an example on the internet (possibly in some documentation), you'll find that this is a great skill to have when documentation is not readily available.

If you found this interesting, be sure to check back for future articles in which I will be covering other useful topics! Also, if you happen to like computer games, be sure to check out GOG's catalogue of cheap, DRM-free games. If you buy games through this link, you will be supporting Programmer's Ranch at the same time.