Deploying snippets within VSIX package

Introduction

If you ask Google, “what are code snippets”, rapidly you will get the following answer:

Code snippets are ready-made snippets of code you can quickly insert into your code. For example, the for code snippet creates an empty for loop. Some code snippets are surround-with code snippets, which enable you to select lines of code, and then choose a code snippet which incorporates the selected lines of code.

I will not get into on how to create code snippets or getting in to details about how do they work. In case you are interested in that, you can read the following article on MSDN. What I’m interested to show here, is not such an obvious thing, that is, how to deploy you snippets via VSIX package and have it registered in Visual Studio as an extension (with all of the benefits of that choice).
Before I started with this task, I have only found a single article speaking about this procedure. On Mads Kristensen’s blog you will find a blog post titled Shipping Visual Studio snippets in an extension. You could now ask yourself, why are you then writing about this? Well, first of all, that blog has several technical issues (at the time of writing) and images are not visualized nor the layout is in place which makes it extremely hard to follow. As the second thing, in order to accomplish successfully this task, that guide misses several important steps which I will try to describe more in detail and moreover give you a working example of code.
There is another way of deploying code snippets and you can find on MSDN the following article about distributing code snippets via Visual Studio Content Installer. I will not go to describe the differences and pros and cons of this two methods. Both of them are valid ways of approaching this problem and based on your situation you should choose the direction to follow.

Background

Before we start, make sure that you have downloaded and installed the Visual Studio SDK. It should match the version of Visual Studio you are using. For the 2013 version you can download it from here.
Once installed, beside other things, you will get some new project templates in Visual Studio, as shown in the following picture:

NewProjectExtensions
At this point we are going to create one snippet that we are going to deploy later on.
Create a text file with a snippet extension, in my case it will be called helloworld.snippet. The content of our file is the following

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>Hello World</Title>
      <Author>Mario Majcica</Author>
      <Description>For demo purposes inserts a simple Hello World in your code</Description>
      <HelpUrl>http://blog.majcica.com/2014/10/27/deploying-snip…n-vsix-package/</HelpUrl>
      <SnippetTypes />
      <Keywords />
      <Shortcut></Shortcut>
    </Header>
    <Snippet>
      <References />
      <Imports />
      <Declarations />
      <Code Language="csharp" Kind="method body" Delimiter="$"><![CDATA[Console.WriteLine("Hello, World!")]]></Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

This is the simplest code snippet ever!
Save it to a convenient location as we are going to use this file later. You can easily create you snippets by a dedicated tool. I found on Codeplex a great freeware tool called “Snippet Editor” which does the job very well.

Great job guys!

Creating the deployment project

Open Visual Studio and create a new VSIX Project. Once you click OK, a new project will be generated and you will be presented with the following screen:

EmptyVSIXProject
What you can see is a typical vsixmanifest designer window (can be different in case you are not using Visual Studio 2013). You can feel free to change this fields accordingly to your needs and you will find plenty of guides about this argument by simply ‘googling’ vsix package. We can close this window for now.
As a next thing we will add a folder in our solution and call it ‘Snippets’. Another sub-folder will be added in my case and I will call it ‘My snippets’. In this sub folder I will add the file we previously created, our helloworld.snippet. It should all look like this:

SnippetsSolution
We are still not done with it. For each code snippet file you added, we need to make sure that the prroperties are set in the right way, otherwise your snippets will not be a part of VSIX package. Right click you code snippet and chage the Build Action to ‘Content’ and Include in VSIX to ‘True’. Once done, it should look like this:

SnippetSettings
You will need to do the same for all the snippets that you added inside this project. Bare in mind that you can select multiple files in the Solution Explorer and do this operation only once.

Once this is done, the only thing we are missing is a way to register our snippets. A package definition can be really handy for this task.

Registering with pkgdef

A package definition (.pkgdef) file is way to encapsulate application configuration information in an easily editable, distributable, and deployable form. It was introduced in Visual Studio 2008 SP1 to support the configuration of Isolated Shell applications. From Visual Studio 2010, .pkgdef files take on a more expanded role and are much more integrated into both the IDE and the Shell (Integrated and Isolated).
Package definition file looks a lot like a .reg file as exported from the Registry Editor. There is one key difference: the root of the registry key (e.g., “HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\12.0“) has been abstracted into the token “$RootKey$”. This, and several other tokenized values for registry keys and paths, allows a given .pkgdef file to be reusable for multiple applications and portable to any machine.
Package files can be easy leveraged by VSIX projects. Once the pkgdef file is added inside the project it is sufficient to add it as an asset inside the VSIX manifest and the job is done. It means it will be executed by the installation process. Let’s check in detail how this is achieved.
First of all create a text file in root of your project and name it snippets.pkgdef. As a content specify the following

// C#
[$RootKey$\Languages\Code\Expansions\CSharp\Paths]
"MySnippets"="$PackageFolder\$Snippets\My Snippets"

As you can see I’m using several tokens (substitution Strings) as $RootKey$ and $PackageFolder$. You can find a list of these strings at the following address.
What this code does is creating a key in the registry, called MySnippets, in the path HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\12.0\Exp_Config\Languages\Code\Expansions\CSharp\Paths
(in our case, which is VS2013 and experimental version, so 12.0\Exp_Config key will defer based on your environment, this also means that the $RootKey$ is replaced dynamically with a different string, depending on the context). The value of my MySnippets key equals to $PackageFolder\$SnippetsMy Snippets where $PackageFolder$ will be substituted by the full path of the directory in which files are deployed.
Once the file is created we need to “wire” it to the VSIX package. Open your manifest designer and move to the Assets tab

Assets
Click on new to add a new asset and set it up in the following way

AddNewAsset
The asset should be of VsPackage type and source should be set to ‘File on filesystem’. For the path choose the path to your .pkgdef file.
Click OK and the job is done.
If you prefer to get here by editing the vsixmanifest without the designer, you can add following inside the PackageManifest element:


  

Deploy and test

We can quickly check if our extension is working as expected. A simple hit to F5 will launch Visual Studio in Experimental mode and deploy our extension. Let’s try it out.
First of all we will check if the extension is deployed. Go to the TOOLS -> Extensions and updates... menu. You should find your extension there. It should look lit similar to the following picture:

ExtensionsAndUpdates
If this is right, second thing to check is if the snippets are correctly registered. Go to the TOOLS -> Code Snippets Manager... or press CTRL + K, CTRL + B.
Once the Code Snippets Manager window opens, in the Language drop down, choose Visual C#, and you should see your snippets registered and visible to Visual Studio as show in the following picture

CodeSnippetManager
You can now build for release your project and distribute your SnippetsCollection.vsix package. All other files you will find in the bin folder are not necessary. Installation is straight forward and if you have decided to support multiple versions of Visual Studio, the only thing you will get prompted is for which version of Visual Studio you whish to install your extension.

Considerations

This article shows a graceful way to distribute your code snippets. It is also a neat solution when it comes to the maintenance as just by increasing the version of the VSIX package you will be able to deploy your changes. If you decide to distribute your VSIX packages through a private gallery, you will make the installation and update as simple as possible for all of your users.
You can read more about this argument here and here.
You also may consider deploying your wsix as part of an installer and soon I will write more about this argument.
In case of debugging problems you can check one of my previous post Resetting the Visual Studio Experimental instance.
If any do not hesitate to comment.

moq.Callback(), The Unknown

Introduction

More and more often I do see people having trouble testing certain type of code. As a result, code coverage is dropping down, unverified logics are shown up, lowering the quality and rising frustrations. This is what pushed me to write this post and describe this kind of situations and a decent solution to it.
This kind of situations are commonly found in all the cases in which the tested unit manipulates the arguments that are passed to our mock. In this case we need to verify that this transformation did what we expected it to do. However, it ain’t that simple and straightforward as it seems. As code speaks more than thousand words, let’s illustrate this with an example.
Consider the following class:

public class ProductService
{
    private readonly IOrderRepository m_orderRepository;

    public ProductService(IOrderRepository orderRepository)
    {
        m_orderRepository = orderRepository;
    }

    public List GetProducts(int customerId, int orderId)
    {
        OrderSearchCriteria orderSearchCriteria = new OrderSearchCriteria
        {
            OrderId = customerId // THIS IS THE PROBLEM WE ARE GOING TO SEARCH FOR

            // Set some other search criteria...
        };

        Order retrievedOrder = m_orderRepository.GetOrder(orderSearchCriteria);

        // Do something else
        return retrievedOrder.Products;
    }
}

What you can see here is a hypothetical product service that we are going to test. More precisely we are going to write unit tests for the GetProducts method. What this method does in particular is composing another object that will be passed to our dependency, the order repository. Now you can argue that this is a bad practice, that the object composition should be handled in a different manner, as usually in this cases the single responsibility principle is not met. And you are right, but we do not live in a perfect world and often we can’t easily change what is already there. However, we need to keep extending and improving our software.

Still, I do need to write a test for that method. What should I do, how do I spot this bug that we just introduced?

There are two ways of writing a unit test that will test, verify and spot our bug. Let’s start with the first one.

Shaping an expected instance

We can tackle this problem by setting up manually, in our test, an instance of OrderSearchCriteria class, as we expect it to be, base on the parameters that we are passing in, and make sure that our mock accepts only an instance that equals ours, on purpose created class.

Let’s check our unit test.

[TestMethod]
public void GetProducts_Creates_OrderSearchCriteria_Correctly()
{
    const int customerId = 56789;
    const int orderId = 12345;

    OrderSearchCriteria orderSearchCriteria = new OrderSearchCriteria
    {
        OrderId = orderId
    };

    Mock orderRepositoryMock = new Mock();
    orderRepositoryMock
        .Setup(m => m.GetOrder(orderSearchCriteria))
        .Returns(new Order());

    ProductService sut = new ProductService(orderRepositoryMock.Object);

    List result = sut.GetProducts(customerId, orderId);
}

Now, first thing first. In order this example to even work, your parameter class needs to implement the equality members. This is necessary as Moq, in order to determine equality of parameters, rightly, relays on Equals() method.

Another disadvantage of this technique is the fact that construction of our own object can sometimes be hard or even not possible. Not to even mention the maintenance problem we are going to introduce.

As Moq in the case of wrong parameter will return a null from the method call, often null value is managed and interpreted as a possible state. In that case it will be very hard or impossible to discover our bug.
Luckily there is a cleaner way to approach this kind of situations.

Extracting the parameter via Callback method

As it is not often used, many developers tend to ignore the Callback method that is provided by Moq framework. In this kind of situations it can be very handy.

Check out the following test.

[TestMethod]
public void GetProducts_Creates_OrderSearchCriteria_Correctly_2()
{
    const int customerId = 56789;
    const int orderId = 12345;

    OrderSearchCriteria recievedOrderSearchCriteria = null;

    Mock orderRepositoryMock = new Mock();
    orderRepositoryMock
        .Setup(m => m.GetOrder(It.IsAny&lt;OrderSearchCriteria&gt;()))
        .Returns(new Order())
        .Callback(o => recievedOrderSearchCriteria = o);

    ProductService sut = new ProductService(orderRepositoryMock.Object);

    List result = sut.GetProducts(customerId, orderId);

    Assert.IsNotNull(recievedOrderSearchCriteria);
    Assert.AreEqual(orderId, recievedOrderSearchCriteria.OrderId);
}

You can see that I’m setting up my mock and I’m specifying what the callback should do. What I’m telling him in this case is that for the parameter of type OrderSearchCriteria, once the method is invoked, copy it to the locally defined object called recievedOrderSearchCriteria. This will give me the possibility to check what came in the call of the GetOrder method, and verify that is what I expect to receive.
Once I start my assertions I do a check on the recievedOrderSearchCriteria and I do make sure that what came in, is what I do expect.

This test will fail and we will succeed in our intent. Also the message that we are receiving is far way clearer than one in the previous example. It states at this moment

Assert.AreEqual failed. Expected:<12345>. Actual:<56789>.

Beside this we are actually asserting the expected result thus specifying behavior in an explicit way.
Now, this on my opinion is much better!

Other considerations about the Callback method

For a less experienced developers, I’ll also make an example of how to get a callback working if you have more then one parameter accepted by your mocked object.
I’m going to extend our IOrderRepository interface by adding a overload of the GetOrder method that accepts two parameters. Also I will implement another method on ProductService class that uses this newly created method.

public interface IOrderRepository
{
    Order GetOrder(OrderSearchCriteria searchCriteria);

    Order GetOrder(int orderId, bool archieved);
}

public List GetProducts(int orderId)
{
    Order retrievedOrder = m_orderRepository.GetOrder(orderId, true);

    return retrievedOrder.Products;
}

In order to mock my order repository and have on callback the necessary values, the following test is used.

[TestMethod]
public void GetProducts_With_Archieved_Orders()
{
    const int orderId = 12345;

    int receivedOrderId = 0;
    bool receivedArchieved = false;

    Mock orderRepositoryMock = new Mock();
    orderRepositoryMock
        .Setup(m => m.GetOrder(It.IsAny(), It.IsAny()))
        .Returns(new Order())
        .Callback((o, a) =>
        {
            receivedOrderId = o; 
            receivedArchieved = a;
        });

    ProductService sut = new ProductService(orderRepositoryMock.Object);

    List result = sut.GetProducts(orderId);

    Assert.AreEqual(orderId, receivedOrderId);
    Assert.AreEqual(true, receivedArchieved);
}

As you can see, I just added an extra type in my generic definition and then adjusted my lambda expression accordingly. If more than two parameters are required, you can just follow the same pattern and define them as many as need. At example .Callback((o, a, i) etc.

You always need to respect the exact parameter signature of the method you are setting up. The number of parameters accepted by the mocked method need to mach in type, order and number the parameters accepted by your mocked method.

There is another way to express the same statement, just by using the lambda expression instead of generics. I can rewrite the above examples in the following way:

.Callback((OrderSearchCriteria o) => recievedOrderSearchCriteria = o);

// ...

.Callback((int o, bool a) =>
{
    receivedOrderId = o;
    receivedArchieved = a;
});

The effect is the same, it is only about your preference which of the two ways to use.

It is also possible to define it before and after the method invocation and as I can’t think of a nice example I will just provide the one from the Moq documentation:

// callbacks can be specified before and after invocation
mock.Setup(foo => foo.Execute("ping"))
    .Callback(() => Console.WriteLine("Before returns"))
    .Returns(true)
    .Callback(() => Console.WriteLine("After returns"));


Conclusion

Each time you need to check the arguments that are passed to the method you are setting up, Callback() will help you get to them. Also, if you need to execute any code before or after the method is invoked, Callback() will let you do so.
Hopefully you are not going to use it on a daily basis, but it is handy to know about it as sooner or later you are going to face a situation where a Callback() will help you achieve your goal.

ASPxGridView Excel style – Extending functionality

Introduction

In this post we will continue adding functionality on our previous example. If you missed my previous post you can get it here: ASPxGridView Excel style – Adding notes to grid cells. The main goal for this time is to add inline editing functionality to the grid, in order to be able to update the imports and manage adding, editing and removing credit cards. However we will add also a couple more enhancements in order to show the potential of the grid and offer benefits to the end users. I will not go in the detail about the environment and developing requirements, if interested jump op to my other posts.

Table of contents

  1. The necessary and the result
  2. Creating database schema
  3. Updating our DataService
  4. In-line editing
  5. Managing Credit Cards
  6. Marking the values in the grid
  7. Exporting the data
  8. Downloads and the source code
  9. Final word

The necessary and the result

The attached project is written in Visual Studio 2012. The express version should be enough. Also you will need a Microsoft SQL Server 2012, Express or and more advanced version will be just fine. The database is part of the project and it will be automatically mounted by VS. You can change this easily be creating your own database and changing the connection string in the web config. Last but not least you will need the 12.2.5 version of DevExpress ASP.NET controls installed on your machine. If you do have a more recent version, upgrading the project should be easy. Check this post about how to get the DevExpress controls and on how to eventually upgrade the project http://blog.majcica.com/2012/08/05/aspxgridview-master-detail-data-presentation-with-context-menus/.

At the end the result should be similar to this, however there is much more, so check this post till the end.
Final Example

You can check it also LIVE. Live version persist the data in the session so each new session will reset all the data you have inserted. Also because of this, it is a bit simplified. If you are interested in the code, I can provide it to you, just ask in comments.

Updating our DataService

As first we will add a couple of new methods in our DataService class. We will write down methods that will allow us to update, add and remove the credit card entities.

public static bool AddNewCreditCard(string creditCardName)
{
    string query = @"INSERT INTO tbl_CreditCards ([Name]) VALUES (@Name)";

    using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand(query.ToString(), cn))
        {
            cmd.Parameters.Add("@Name", SqlDbType.NVarChar).Value = creditCardName;

            cn.Open();
            return cmd.ExecuteNonQuery() > 0;
        }
    }
}

public static bool UpdateCreditCard(int creditCardId, string creditCardName)
{
    string query = @"UPDATE tbl_CreditCards SET [Name] = @Name WHERE Id = @creditCardId";

    using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand(query.ToString(), cn))
        {
            cmd.Parameters.Add("@creditCardId", SqlDbType.Int).Value = creditCardId;
            cmd.Parameters.Add("@Name", SqlDbType.NVarChar).Value = creditCardName;

            cn.Open();
            return cmd.ExecuteNonQuery() > 0;
        }
    }
}

public static string GetCreditCard(int creditCardId)
{
    string query = @"SELECT [Name] FROM tbl_CreditCards WHERE Id = @creditCardId";

    using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand(query.ToString(), cn))
        {
            cmd.Parameters.Add("@creditCardId", SqlDbType.Int).Value = creditCardId;

            cn.Open();
            return (string)cmd.ExecuteScalar();
        }
    }
}

public static bool RemoveCreditCard(int creditCardId)
{
    string query = @"UPDATE tbl_CreditCards SET [Active] = 0 WHERE Id = @creditCardId";

    using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand(query.ToString(), cn))
        {
            cmd.Parameters.Add("@creditCardId", SqlDbType.Int).Value = creditCardId;

            cn.Open();
            return cmd.ExecuteNonQuery() > 0;
        }
    }
}

This code is pretty straight forward. The only exception is the RemoveCreditCard method, which actually doesn’t delete the item and in cascade the related items in the tbl_Import, but only flags the credit card as inactive. In order to make this thing actually work, we need to modify the stored procedure that we defined in previous post sp_GetCreditCardImports.
I will not waste space in this post so I will show you only what you would need to add.

...
WHERE tbl_CreditCards.Active = 1
...

Now is the turn of writing down methods that will Add, Update and Remove imports.

public static bool RemoveImport(int creditCardId, int year, byte month)
{
    string query = @"DELETE FROM tbl_Imports WHERE CreditCardId = @creditCardId AND [Year] = @year AND [Month] = @month";

    using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand(query.ToString(), cn))
        {
            cmd.Parameters.Add("@creditCardId", SqlDbType.Int).Value = creditCardId;
            cmd.Parameters.Add("@year", SqlDbType.Int).Value = year;
            cmd.Parameters.Add("@month", SqlDbType.SmallInt).Value = month;

            cn.Open();
            return cmd.ExecuteNonQuery() > 0;
        }
    }
}

public static bool AddImport(int creditCardId, int year, byte month, decimal import)
{
    string query = @"INSERT INTO tbl_Imports ([CreditCardId], [Year], [Month], [Import])
                VALUES (@creditCardId, @year, @month, @import)";

    using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand(query.ToString(), cn))
        {
            cmd.Parameters.Add("@creditCardId", SqlDbType.Int).Value = creditCardId;
            cmd.Parameters.Add("@year", SqlDbType.Int).Value = year;
            cmd.Parameters.Add("@month", SqlDbType.SmallInt).Value = month;
            cmd.Parameters.Add("@import", SqlDbType.Money).Value = import;

            cn.Open();
            return cmd.ExecuteNonQuery() > 0;
        }
    }
}

public static bool UpdateImport(int creditCardId, int year, byte month, decimal import)
{
    string query = @"UPDATE tbl_Imports SET [Import] = @import
                WHERE CreditCardId = @creditCardId AND [Year] = @year AND [Month] = @month";

    using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
    {
        using (SqlCommand cmd = new SqlCommand(query.ToString(), cn))
        {
            cmd.Parameters.Add("@creditCardId", SqlDbType.Int).Value = creditCardId;
            cmd.Parameters.Add("@year", SqlDbType.Int).Value = year;
            cmd.Parameters.Add("@month", SqlDbType.SmallInt).Value = month;
            cmd.Parameters.Add("@import", SqlDbType.Money).Value = import;

            cn.Open();
            return cmd.ExecuteNonQuery() > 0;
        }
    }
}

This is it. No more code is necessary for managing data and we can now comfortably start with the interface.

In-line editing

Editing the ASPxGridView is very easy. There are several methods for achieving the same result. If you are interested in more you can check this Grid Editing – Edit Modes
A rich set of editors for different column types allows column values to be modified with ease. Still there are a couple of tricks that we will see in the following lines.
In order to start editing a certain row, we can choose the way we are going to start this action. At example you can add a Command Button in the grid itself or simply start editing let’s say on a row double click. There are also many other modes, you have rich client and server side methods in order to start editing one or more rows. Still I will let you discover them and invent new ones. We are going to make our row editable on double click and in order to achieve that we will write down a function (in JavaScript) that is going to be associated to the grids client side event called RowDblClick. Let’s see how this is done.
At first we will add in our ASPX file, inside the grid definition a new section that is called ClientSideEvents (as we already did for other controls) and it will look like:


Then we will write down the JavaScript method itself:

function gvPrevisions_RowDblClick(s, e) { s.StartEditRow(e.visibleIndex); }

It is that simple! Just call StartEditRow JavaScript method that is defined on the grid (in this case I’m using s and not the ClientInstanceName in order to access the grid methods, but here is basically the same) and pass the visible index of the row that we intend to put in edit mode. Kindly this even will provide us the visible index via the function parameter which will indicate the row that user double clicked. If you run the example right now and double click on a row, you should see something similar to this:
Grid in Edit Mode
Even if this looks nice and simple, you maybe noticed that the fields are not editable. This is because we didn’t binded our grid to a valid and well defined Data Source. Because of this, we have our edit controls in read only state. You can find on Devexpress support web site several issues that are speaking about this behavior and why of it. The good thing is that we can solve this easily. Define the following server side event:

protected void gvImports_CellEditorInitialize(object sender, ASPxGridViewEditorEventArgs e)
{
    if (e.Column.FieldName != "Name" || e.Column.FieldName != "Total")
    {
        e.Editor.ReadOnly = false;

        ASPxTextBox box = e.Editor as ASPxTextBox;
        box.HorizontalAlign = HorizontalAlign.Right;
        box.MaskSettings.Mask = "$ <0..99999g>.<00..99>";
        box.MaskSettings.IncludeLiterals = MaskIncludeLiteralsMode.DecimalSymbol;
        box.ValidationSettings.ErrorDisplayMode = ErrorDisplayMode.ImageWithTooltip;
        box.ValidationSettings.Display = Display.Dynamic;
        box.ClientSideEvents.KeyDown = "gvImports_Cell_KeyDown";

        if (e.Column.FieldName == "1")
            box.Focus();
    }
}

What we defined here is checking if the cells that are going in edit mode are the one we are aiming to put in edit (not the Name and Total columns) and if so we are setting a couple of properties. As first, we are indicating the cell as read only, after that we are casing the control to an ASPxTextBox (which is the right control for these columns) and setting several parameters in order to make the editing look proper. The most important thing is that we are assigning to each of editing cells a client side event KeyDown, in which we will manage the persistence. Let’s see how this important method is defined.

function gvImports_Cell_KeyDown(s, e) {
    switch (e.htmlEvent.keyCode) {
        case 13:
            ASPxClientUtils.PreventEventAndBubble(e.htmlEvent);
            gvImports.UpdateEdit();
            break;
        case 27:
            gvImports.CancelEdit();
            break;
    }
}

If Enter (13) key is pressed meanwhile we are in one of editing cells, we will prevent the defaults for this action and call UpdateEdit method on our grid. In case the user hits Escape key (27) we will just get out of the edit mode by calling CancelEdit method. Once UpdateEdit is called a callback will be generated and the RowUpdating server side method will be executed, in which we will need to handle the persistence of the data. We need to attach the following method to the RowUpdating event:

protected void gvImports_RowUpdating(object sender, ASPxDataUpdatingEventArgs e)
{
    // copy OrderedDictionaries to Hashtables
    // for easier data manipulations
    Hashtable newValues = new Hashtable();
    Hashtable oldValues = new Hashtable();

    foreach (DictionaryEntry item in e.NewValues)
    {
        // if value == 0 then do not insert any value
        newValues.Add(Convert.ToByte(item.Key), (decimal)item.Value == 0 ? null : (decimal?)item.Value);
    }

    foreach (DictionaryEntry item in e.OldValues)
    {
        // transform DbNull to null
        oldValues.Add(Convert.ToByte(item.Key), item.Value == DBNull.Value ? null : (decimal?)item.Value);
    }

    int creditCardID = (int)e.Keys[0];
    int year = (int)cbYears.SelectedItem.Value;

    for (byte i = 1; i <= newValues.Count; i++)
    {
        if ((decimal?)newValues[i] != (decimal?)oldValues[i])
        {
            if (newValues[i] == null)
            {
                // The value was removed
                // so remove the record from table
                DataService.RemoveImport(creditCardID, year, i);
                continue;
            }

            if (oldValues[i] == null)
            {
                // The value was inexistant
                // add record to the table
                DataService.AddImport(creditCardID, year, i, (decimal)newValues[i]);
                continue;
            }

            // it's a change
            // update the record in the table
            DataService.UpdateImport(creditCardID, year, i, (decimal)newValues[i]);
        }
    }

    e.Cancel = true;
}

What happens in this event is that a request for updating rows is sent. We will receive the old values that (of interested updating columns) and the new one that were chosen by the user, in a form of OrderedDictionaries, exposed by two properties in our event argument. The properties are called logically NewValues and OldValues. As OrderedDictionaries are not so handy for processing, I will create two Hashtables and copy the data for both properties from OrderedDictionaries to Hashtables. Meanwhile I'm copying, I will cast eventual values to a nullable decimals and if the value equals zero, I will just insert null instead of the value. Next is retrieving other necessary data as kredit card ID (stored as editing row key and exposed to as always via the event argument) and the current year, which we will get out of our check box.
Now we will process all of our columns values one by one. If the specific column value in Hashtable that contains old values do not match to the one in the new values Hashtable, it means that the value for that column is changed and we need to determine what kind of change occurred before we decide which action to take. If the new value is null it means that we need to remove this item from database therefor we will invoke the appropriate method that we created earlier and pass all the parameters as requested. If the old value is null, it means that we do not have an entry for this item in our database, therefor we will add a new item via a dedicated method. At last if the value exists in both Hashtables we will need to update the value to the new one.
At the end we will set the Cancel property of our event argument to false. The e.Cancel = true allows us to prevent the ASPxGridView from trying to update data by the control itself. There is one small detail left, after we persist the data, we should indicate to the grid that editing is over so it can get back in the non edit mode. The best thing to achieve this is to call CancelEdit method on server side each time we bind the grid (even id sometimes it is not necessary it is not a big overhead and makes sure that each time we update data we make sure that the grid is not in edit mode). Consequently our gvImports_DataBinding event will change in the following way:

protected void gvImports_DataBinding(object sender, EventArgs e)
{
    int yearToBind = DateTime.Now.Year;

    if (cbYears.SelectedItem != null)
        yearToBind = Convert.ToInt32(cbYears.SelectedItem.Value);

    gvImports.DataSource = DataService.GetAllByDetail(yearToBind);
    gvImports.CancelEdit();
}

Congratulations, you can now edit this grid values! Smooth, isn't it? 🙂

Managing Credit Cards

As the next requirement we will add the possibility to add new credit cards, update the name of the existing ones or remove them. As for the notes we will add a new pop-up windows which will help us to perform this operations. Defining the popup window is straight forward.


    
        
            

As for the add note popup window, we defined the similar structure for add credit card popup. The main difference is in the validation of the credit card name. We defined the field as mandatory and client side validation will be performed for that field. If empty an error message will appear. Now I will show you the code for all of the three new scripts that we attached to our client side events.

function PersistCreditCard() {
    var isValid = ASPxClientEdit.ValidateEditorsInContainer(null, 'AddCreditCard');

    if (isValid) {
        var command = 'AddNewCreditCard';

        if (currentIsCreditCardEdit)
            command = 'EditNewCreditCard';

        popupAddCreditCard.Hide();
        gvImports.PerformCallback(command + '|' + currentVisibleIndex);
    }
}

function txtAddCreditCard_KeyDown(s, e) {
    switch (e.htmlEvent.keyCode) {
        case 13:
            ASPxClientUtils.PreventEventAndBubble(e.htmlEvent);
            PersistCreditCard();
            break;
        case 27:
            popupAddCreditCard.Hide(); txtAddCreditCard.SetText('');
            break;
    }
}

function btnAddNewCreditCard_Click(s, e) { PersistCreditCard(); }
function btnCancelNewCreditCard_Click(s, e) { popupAddCreditCard.Hide(); txtAddCreditCard.SetText(''); }

The technique is the same used in the previous example, on editing and adding the imports. In case that the client side validation is performed and user have imputed the valid entries, we will preform a callback on the grid (so it can be also automatically refreshed). In the callback we will specify if the operation we are preforming is an edit or an add and in case of the edit pass the selected row index. What we are missing right now is the server side code for persisting the newly added values. To achieve that we are going to modify previously define gvImports_CustomCallback event (it is defined in the previous blog post that you can find here. It will look like this:

protected void gvImports_CustomCallback(object sender, ASPxGridViewCustomCallbackEventArgs e)
{
    // if no parameter is passed from cliend side, do no process
    if (string.IsNullOrEmpty(e.Parameters))
        return;

    int year = (int)cbYears.SelectedItem.Value;

    string[] values = e.Parameters.Split('|');
    string command = values[0];
    int visibleIndex = 0;
    int creditCardID = 0;

    if (values.Length > 1)
    {
        visibleIndex = int.Parse(values[1]);
        if (command != "ChangeYear")
            creditCardID = (int)gvImports.GetRowValues(visibleIndex, "Id");
    }

    byte month = 0;

    if (values.Length > 2)
        month = byte.Parse(values[2]);

    switch (command)
    {
        case "AddNote":
            DataService.AddNewNote(creditCardID, year, month, txtNote.Text);
            break;
        case "DeleteNote":
            DataService.DeleteNote(creditCardID, year, month);
            break;
        case "EditNote":
            DataService.UpdateNote(creditCardID, year, month, txtNote.Text);
            break;
        case "ChangeYear":
            gvImports.DataSource = DataService.GetAllByDetail(Convert.ToInt32(values[1]));
            break;
        case "AddCreditCard":
            DataService.AddNewCreditCard(txtAddCreditCard.Text);
            break;
        case "EditCreditCard":
            DataService.UpdateCreditCard(creditCardID, txtAddCreditCard.Text);
            break;
        case "DeleteCreditCard":
            DataService.DeleteCreditCard(creditCardID);
            break;
    }

    gvImports.DataBind();
}

Based on the command we received, we will analyse other values passed as parameters and call the service method that is associated with the current action. At the end we will rebind the grid so the newly added values can be visible. Simple and straight forward, isn't it?
I will again mention the same trick I used for the editing, and it is here for the same reason I explained earlier when I was explaining the editing of the note, but this time is made on the Add/Edit Credit Card popup. On edit we will retrieve the selected credit card name and we will populate the text box with that obtained value.

protected void popupAddCreditCard_WindowCallback(object source, PopupWindowCallbackArgs e)
{
    string[] values = e.Parameter.Split('|');

    int visibleIndex = int.Parse(values[0]);
    int creditCardID = (int)gvImports.GetRowValues(visibleIndex, "Id");

    txtAddCreditCard.Text = DataService.GetCreditCard(creditCardID);
    txtAddCreditCard.Focus();
}

Marking the values in the grid

Meanwhile I was writing the code for the previous requirements I got an idea. Why not let give to user the possibility to put in evidence the values that are lower than the certain amount? Well it is a simple task and it will maybe give you the inspiration to expand this example in other ways. Let's see how to achieve something similar.
First of all we need to create the necessary interface and in order to accomplish that we will define the following controls in our aspx file. After the definition of the GridView add the following:

As you can see, apart for the table I used to achieve a certain layout, I added a text box in which the user will specify the value that we will used as the upper limit for our cells that will be placed in evidence. Also I used a ASPxColorEdit so the user can choose the color he prefers for marking the cells. A clear and confirm button are also there. Then I associated the following code to the relevant client side events (only button clicks in our case):

function btnMark_Click(s, e) {
    gvImports.PerformCallback('Mark');
}

function btnClear_Click(s, e) {
    txtMarkValue.SetText('');
    ceMark.SetColor('#C0C0C0');
    gvImports.PerformCallback('Mark');
}

In case of clear click, I will reset the values of the color chooser and the value text box to their default, then perform a callback so if any value was previously set, it can be restored to the default value. In case of click on mark button, I will perform a callback on the grid and specify the 'Mark' command. Once the callback is performed and the gvImports_CustomCallback event is triggered, you will see that I do not treat the Mark command. This is because it is not necessary at this point. The only important thing is to rebind the grid. Instead in the gvImports_HtmlDataCellPrepared I will add the following at the end of the method:

protected void gvImports_HtmlDataCellPrepared(object sender, ASPxGridViewTableDataCellEventArgs e)
{
    ...

    decimal markValue = 0;
    decimal.TryParse(txtMarkValue.Text, out markValue);

    if (markValue != 0 && (decimal)e.CellValue <= markValue)
    {
        e.Cell.BackColor = ceMark.Color;
    }
}

Whit this I will check if a mark value is set and if it is a valid number I will then check the cell I'm processing in this moment. In case the cell value is less or equal to the value specified in our text box, I will change the background color of that cell and set it to the color that is indicated in our color chooser.
This shows you how can you approach the grid to add extra functionality with ease.

Exporting the data

It will all be nice but useless in case we could not retrieve our data and persist it locally or/and edit it for a presentation or maybe import it in our accounting software. Together wit the Grid, DevExpress offers us an additional control that will handle the exporting procedures. It is easy to setup and the results are great! Let's check how to do it.
First of all we need to add this control in our page. Drag and drop from the toolbox the ASPxGridViewExporter control. Then edit your aspx code and set it to mach the following:



You will already notice some properties that we specified. In order to indicate the grid from which we would like to export the data we need to specify GridViewID property and as it name suggest, set id to the grid view ID. Also we will specify the PaperKind and Landscape property to get the correct formatting of our export. Now we need some button that will request the export in different formats. There are several formats that ASPxGridViewExporter can manage by default. Check the following link for further details Exporting Data - Exporting to PDF, XLS, XLSX and RTF. We will add them to the same line in which the year selector is collocated. The final code will look like this:

We added three buttons and set some of the default and well known properties as image and text. What you should notice here is the UseSubmitBehavior property set to false. In this way the button will not cause the browser's submit mechanism. However a post-back will still be performed. So for each button we will define the event handler on the server side and it will look like this:

#region Export
protected void btnExportExcel_Click(object sender, EventArgs e)
{
    gridExport.FileName = string.Format("{0} {1}", "Credit card imports for year: ", cbYears.SelectedItem.Value);
    gridExport.WriteXlsToResponse();
}

protected void btnExportPdf_Click(object sender, EventArgs e)
{
    gridExport.FileName = string.Format("{0} {1}", "Credit card imports for year: ", cbYears.SelectedItem.Value);
    gridExport.WritePdfToResponse();
}

protected void btnExportCsv_Click(object sender, EventArgs e)
{
    gridExport.FileName = string.Format("{0} {1}", "Credit card imports for year: ", cbYears.SelectedItem.Value);
    gridExport.WriteCsvToResponse();
}
#endregion

On click of each of the defined buttons we will set the file name on a more meaningful string and we will call the respective method on the ASPxGridViewExporter. That is! Check the result!

excel2

The final result is pleasing. We added some great functionality with a little effort thanks to the possibilities offered by DevExpress controls.

Downloads and the source code

You can find the source code of my project for download here.
You can find a trial version of the requested controls here.

Final word

If I was not clear or I gave for granted some of the explanations feel free to comment and I will do my best in order to bring a proper explanation. I didn't went in detail on all of the code as most of it was covered in the previous post, ASPxGridView Excel style – Adding notes to grid cells. If there will be interest I will further expand this example and introduce you to other nice features that we can easily lay down with DevExpress controls. Stay tuned and happy programming!