grid.ColWidth[nCol] = yy
for each column. If you do this, then the user would be able to size the grid columns without further work on your part, and you could set explicit initial sizes.
This would assume your datasource had relatively few columns and the only need for the virtual grid was with respect to having many rows (or some other reason not pertaining to columns).
If your grid has too many columns to allow the explicit setting of ColCount, then the next suggestion would be to create a hashtable with keys being a column index and values being the size. Then add your explicitly sized columns to this hashtable. Then handle the QueryColWidth event. In the handler, check whether e.Index is a key in your hashtable. If so, then get the width value from the hashtable, and set e.Size to this value (also setting e.Handled).
This will take care of the initial sizing. To support user resizing, you would catch the ColWidthsChanged event, and in your handler, get the columns changing from the events args, check if they are in your hashtable. If so, then set the new width to the existing key in the hashtable. If not, then add the colindex and value to the table.
for (int ix = e.From; ix <= e.To; ix++)
{
m_ColumnSize[ix] = e.Values[ix - e.From];
e.Cancel = true;
this.Refresh();
}