Introduction
The DataGridView
shipped with the framework allows the user to reorder the columns but there is nothing to persist the new column order.
In this article, I'll show an enhanced DataGridView
that saves:
- column order
- column width
- column visibility
to the user.config
without the need to code something in the hosting form.
Background
Usually the application and user-settings are created via the designer in Visual Studio. Behind the scenes, the designer generates code for accessing the settings.
For this control, the code for accessing the settings is generated manually so it's possible to use more advanced datatypes than the few primitive ones provided in the designer.
Because it is possible to have more than one DGV in the client application the settings are stored with the use of a dictionary. The key of the dictionary is the name of the control.
Binary serialization is used because the dictionary implements the IDictionary interface and thus it is not possible to use XML serialization. But this doesn't matter - the result is the same. The only difference is that the information in the user.config is not as readable as with XML serialization.
In .NET assemblies are loaded into an AppDomain and executed from there. Each AppDomain has one configuration-file (not necessarily) and therefore the data of several configuration classes get serialized into this config-file (or read from).
Using the Control
The usage of the control is not different from the System.Windows.Form.DataGridView
.
The Code of the Control
The control is derived from the System.Windows.Form.DataGridView
. So everything the DataGridView
provides can be provided by this control too.
Attributes of the Class
[Description("Extension of the System.Windows.Forms.DataGridView")]
[ToolboxBitmap(typeof(System.Windows.Forms.DataGridView))]
public class gfDataGridView : System.Windows.Forms.DataGridView
{
...
}
The attributes are used for displaying a bitmap and a description of the control in the toolbox - like usual controls do.
Saving the Column Order, Width and Visibility
This is done in the Dispose
method. For storing the data, the ColumnOrderItem
class is used (see later in this article).
protected override void Dispose(bool disposing)
{
SaveColumnOrder();
base.Dispose(disposing);
}
private void SaveColumnOrder()
{
if (this.AllowUserToOrderColumns)
{
List<ColumnOrderItem> columnOrder = new List<ColumnOrderItem>();
DataGridViewColumnCollection columns = this.Columns;
for (int i = 0; i < columns.Count; i++)
{
columnOrder.Add(new ColumnOrderItem
{
ColumnIndex = i,
DisplayIndex = columns[i].DisplayIndex,
Visible = columns[i].Visible,
Width = columns[i].Width
});
}
gfDataGridViewSetting.Default.ColumnOrder[this.Name] = columnOrder;
gfDataGridViewSetting.Default.Save();
}
}
At first step, it gets checked whether AllowUserToOrderColumns
is enabled or not. If not, there is no necessity to save the column order. This can be omitted if you want to save the width, etc. of the columns.
Then a List
is populated with the values that should be saved to user.config and this list gets added or updated to the dictionary where the key is the name of the control. The dictionary gets assigned to the property setting and saved - just like "normal" designer generated settings.
Restoring the Column Order
In the OnCreateControl
method the column order gets restored. It's important to start with the first displayed column to get the desired result. If this would be done using the column index, then it's just like dragging a column from one position to another which rearranges the order and the result is wrong.
protected override void OnCreateControl()
{
base.OnCreateControl();
SetColumnOrder();
}
private void SetColumnOrder()
{
if (!gfDataGridViewSetting.Default.ColumnOrder.ContainsKey(this.Name))
return;
List<ColumnOrderItem> columnOrder =
gfDataGridViewSetting.Default.ColumnOrder[this.Name];
if (columnOrder != null)
{
var sorted = columnOrder.OrderBy(i => i.DisplayIndex);
foreach (var item in sorted)
{
this.Columns[item.ColumnIndex].DisplayIndex =
item.DisplayIndex;
this.Columns[item.ColumnIndex].Visible = item.Visible;
this.Columns[item.ColumnIndex].Width = item.Width;
}
}
}
ColumnOrderItem
The items of the list are defined within this class:
[Serializable]
public sealed class ColumnOrderItem
{
public int DisplayIndex { get; set; }
public int Width { get; set; }
public bool Visible { get; set; }
public int ColumnIndex { get; set; }
}
Settings
As mentioned earlier in this article, the designer generates usually the code for the settings-class. Here this is done manually and for storing the column order, a Dictionary<string, List<ColumnOrderItem>>
is used. This dictionary gets serialized as binary.
internal sealed class gfDataGridViewSetting : ApplicationSettingsBase
{
private static gfDataGridViewSetting _defaultInstace =
(gfDataGridViewSetting)ApplicationSettingsBase
.Synchronized(new gfDataGridViewSetting());
public static gfDataGridViewSetting Default
{
get { return _defaultInstace; }
}
[UserScopedSetting]
[SettingsSerializeAs(SettingsSerializeAs.Binary)]
[DefaultSettingValue("")]
public Dictionary<string, List<ColumnOrderItem>> ColumnOrder
{
get { return this["ColumnOrder"] as Dictionary<string,
List<ColumnOrderItem>>; }
set { this["ColumnOrder"] = value; }
}
}
The upper part is just the singleton implementation of the settings class. Then the dictionary for storing the column order is defined. The attributes specify that the property:
- should be stored to the user scope (user.config)
- serialization mode: binary
- and the default value is an empty
string
, i.e. an empty dictionary
That's everything needed.
History
- 29th July 2009 (Version 1.0.1.1)
Updated article and code.
- The problem of having more than one DGV in the client application is solved (due the use of the dictionary).
- The DGV can be used in several containers (due to setting the columnorder in the
OnCreateControl
method and due to saving the columnorder in theDispose
method.
- 8th June 2009 (Version 1.0.0.0)
Initial release.