Wednesday, November 26, 2008

DataGrid custom sorting

As you know DataGrid can sort your items by default when column headers are clicked.
You can however implement your own custom sorting.

When you bind your DataGrid instance to a source object, beside other things it will also look to see if your object implements ICollectionView interface. If it does, it will use it when you click the column headers, if it doesn't it will create and use an internal ICollectionView implementation.

The definition for ICollectionView in MSDN is
"Enables collections to have the functionalities of current record management, custom sorting, filtering, and grouping."
From my investigation, the DataGrid control will only use the custom sorting from your ICollectionView implementation.

To implement custom sorting you need to implement:
1. CanSort property:
bool CanSort { get; }

2. SortDescriptions property:
SortDescriptionCollection SortDescriptions { get; }

The DataGrid control will first call the CanSort property of your ICollectionView implementation. If it returns true it will then call SortDescriptions to get the SortDescriptionCollection collection to use when sorting is triggered.

SortDescriptionCollection is a collection of SortDescription objects. When you click on a column header to sort the items by the column, what DataGrid does is to add a new SortDescription object to the SortDescriptionCollection. If you click again on another column the previous SortDescription object is removed from the SortDescriptionCollection and a new SortDescription object is added to the SortDescriptionCollection collection.
Practically, the
SortDescriptionCollection is the representation of the sorting glyph icons you see drawn on the column headers.

A SortDescription object is an object with 2 properties: a property name and sorting direction.
The property name is the name of the property to sort the list by.

That being said, let's write a simple implementation of a simple data source (a list of integers) for a DataGrid control which also implements ICollectionView.

public class MyDataSource : List, ICollectionView
SortDescriptionCollection sortDescColl = new MySortDescriptionCollection();
bool CanSort
return true; // this indicates the DataGrid control that we have a valid SortDescriptionCollection returned by SortDescriptions

SortDescriptionCollection SortDescriptions
return sortDescColl;

We create a new class MySortDescriptionCollection derived from SortDescriptionCollection just to override the InserItem to catch when new columns should get sorted.

public class MySortDescriptionCollection : SortDescriptionCollection
    protected override void InsertItem(int index, SortDescription item)
// new column was clicked to be sorted
// here we should operate the changes to the
MyDataSource collection

MySortDescriptionCollection will need to notify MyDataSource somehow about the sorting.
You can use do this with a public event for example. Have the MySortDescriptionCollection to expose a public event for example.

Tuesday, September 16, 2008

Extract all files from MSI

msiexec /a msifilepath /qb TARGETDIR=folderpath

Example: msiexec /a c:\testfile.msi /qb TARGETDIR="c:\my test\"

Wednesday, July 30, 2008

'AG_E_RUNTIME_METHOD : Begin' error when starting a storyboard

Before starting animation, always make sure that animation objects and objects participating in the animation are added to the application's main object tree.
var myControl = plugIn.content.createFromXaml(a.loader.GetResponseText(part), true);

On Silverlight 1, this code will result an (infamous) error: AG_E_RUNTIME_METHOD : Begin

The solution is simple, add the created object first to main object tree and then start the animation:
var myControl = plugIn.content.createFromXaml(a.loader.GetResponseText(part), true);

This is obvious when looking to simple code like this, but in a bigger application it might not look so.
It's 'interesting' that in Silverlight 2.0 this error does not appear.

Thursday, March 13, 2008

vtable and __declspec(novtable) (aka ATL_NO_VTABLE)

__declspec(novtable) is a storage-class attribute and Microsoft specific applied to classes declaration.
It's purpose is to tell the compiler to not add the default vtable initialization\deinitialisation code in the class ctor\destructor.
When the compiler sees a class with virtual methods, it automatically adds vtable init\deinit code in the class ctor\destructor methods. Remember that the vtable is pointer to an array of pointers of the class virtual methods. I guess the initializatio is needed at the ctor time because at the time when your ctor method code is entered, the inner workings of the C++ object need to be initialized. Note that although the vtable is available on thee ctor code, its useless to use it, i.e. calling any virtual method. This is because the vtable of any derived class is not yet initialized.

So, using __declspec(novtable) we will not have the vtable initialized\deinitialized in the ctor\destructor methods.
This means that we cannot use the vtable in any way. More specifically, it means we cannot call any of the object's virtual method.

This has interesting effects. The obvious use is for interfaces, classes with pure virtual functions.

class __declspec(novtable) IFoo
virtual ~IFoo()

virtual void foo() = 0;

class FooImpl : public IFoo
virtual void foo();

IFoo* p = new FooImpl();

So the ctor\dctor of IFoo doesn't have any vtable initialization code as FooImpl has.
When we call foo() on the object the compiler sees that we use a pointer to an object and the method is virtual. Hence, it will use the vtable to get to the method. Vtable is a hidden pointer data in a class. It is always the first data when looking at the raw object in memory.
The FooImpl ctor initialized the vtable so the call is legit and always succeeds.
Remember that although the we call the Foo method of a pointer to IFoo, the vtable hidden data points to the FooImpl.
The vtable pointer is always the same in the raw object in all class derivations.

Another legit code is the following one:

class __declspec(novtable) A {
virtual void func1() { cout<<1; }
virtual void func2() { cout<<2; }
virtual void func3() { cout<<3; }

class A1 : public A {
// no func1, func2, or func3

It is OK to write:
A* p = new A1();

because A1 ctor initialize the object vtable.

And its NOT OK to write:
A* p = new A();
A->func1(); // undefined behaviour
p->func2(); // undefined behaviour
p->func3(); // undefined behaviour

It is OK to write:
A p;

because calling methods not using a pointer does not make use of vtable.

When using the novtable specifier, always think if the vtable is needed.
For example, you cannot use novtable for a class calling virtual methods in ctor\destructor. This is because the novtable told the compiler to omit the code which initialize the vtable before entering the ctor code.

This is very well described in the paper:

References in favor of pointers use

Always try to use reference instead of pointer to data wherever is possible.
A pointer to a data always raises the question if the pointer holds valid data, if it's 0 (NULL) for instance.
A reference shows that an object is passed and we don't do any data validation anymore, as for pointers.

// foo needs an instance of Foo
void foo(Foo* pF)
_ASSERTE(pF != 0);

// foo needs an instance of Foo
void foo(Foo& f)

It makes code writing and reading easier.

Using pointer variable is necessarily though in some situations.
You might want an optional parameter to your method:

// foo does NOT need an instance of Foo
void foo(Foo* pF)
if(pf != 0)
// use Foo object
// do something else in absence of a Foo object

This coding style and other good ones are mentioned in the paper
The paper is mentioned on the home page of the father of C++.

Monday, February 04, 2008

MSI remember-to's

I am working with a wix installer and i needed to perform a specific task on uninstall.
After coding and compiling the msi, I tried to uninstall the product but I got a nasty error saying that the uninstallation cannot continue. The first thing i thought was 'shit, I cannot uninstall it'.
But here's a solution: MSI copies the original MSI file in the folder:
and the filename is in the form of 9c2ebd.msi
You can sort the files by 'Creation Date' if you know when did u install it.

I used then Orca editor to remove the uninstall custom actions which caused the uninstall to be unsucessful.

Also, another useful to know location is
this is the location where the MSI log are written during installation\uninstallation. sort files by 'Creation Date'to see the log.