Save and Load a DataGrid's Columns Graphical Properties in WPF

Introduction

We'll see in this article how a user can customize a DataGrid, ordering columns or modifying their width, saving those changes for later use (in other words, when the program will show again a specific grid). In the following I'll present a class that does that, with a simple use scenario. The code was commented to be as clear as possible. The function is declared Shared to facilitate the static use of the class. We'll use mainly DataSet and DataTable classes, to benefit from the handy XML access methods, through which we'll realize the storage of column parameters.

Sample Class

  1. Imports System.Data  
  2. Public Class emDataGridOptions  
  3.     ' We declare a certain path in which we'll store the grid's options file  
  4.     Const _DATAGRID_OPTIONS_DIR As String = "C:\tmp\"  
  5.     ' As file extension, we'll use ".di"  
  6.     Const _DATAGRID_OPTIONS_EXT As String = ".di"  
  7.        
  8.     ' ===============================================================================  
  9.     ' Private function to return the full path of options file  
  10.     ' ===============================================================================  
  11.     Private Shared Function ComposeGridOptionsFile(gridName) As String  
  12.         ' Simple concatenation of directory, grid's name, and default extension  
  13.         Return _DATAGRID_OPTIONS_DIR & gridName & _DATAGRID_OPTIONS_EXT  
  14.     End Function  
  15.     
  16.     ' ===============================================================================  
  17.     ' Public shared sub to save DataGrid's option  
  18.     ' ===============================================================================  
  19.     Public Shared Sub SaveGridOptions(dg As DataGrid)  
  20.         ' We'll create a DataSet with the same name of the Grid, generating inside it a DataTable named "columns"  
  21.          Dim columns As New DataSet(dg.Name)  
  22.         Dim coltable As New DataTable("columns")  
  23.     
  24.         ' Here we'll save only some parameters, such as the visibility, the sort direction, etc.  
  25.         ' we must create in the DataTable a number of columns equals to the number of property we intend to save.  
  26.         With coltable  
  27.             .Columns.Add("DisplayIndex", Type.GetType("System.Int32"))  
  28.             .Columns.Add("Width", Type.GetType("System.Double"))  
  29.             .Columns.Add("Visibility", Type.GetType("System.Int32"))  
  30.             .Columns.Add("SortDirection", Type.GetType("System.Int32"))  
  31.         End With  
  32.     
  33.         columns.Tables.Add(coltable)  
  34.    
  35.         ' We execute a loop on the DataGrid's columns, adding to the DataTable a number of rows equals to the  
  36.         ' count of DataGrid's columns, each one of them exposing the value of the property itself          
  37.         For Each c As DataGridColumn In dg.Columns  
  38.             coltable.Rows.Add(New Object() {c.DisplayIndex,  
  39.                                             c.Width.DisplayValue,  
  40.                                             c.Visibility,  
  41.                                             c.SortDirection})  
  42.         Next  
  43.     
  44.         ' Then, using the WriteXml() method, we save an XML file which contains the columns extracted values  
  45.         columns.WriteXml(ComposeGridOptionsFile(dg.Name))  
  46.     End Sub  
  47.     
  48.     ' ===============================================================================  
  49.     ' Public shared sub to load DataGrid's option  
  50.     ' ===============================================================================  
  51.     Public Shared Sub LoadGridOptions(dg As DataGrid)  
  52.         ' We check if the options file exists...  
  53.          If Not (IO.File.Exists(ComposeGridOptionsFile(dg.Name))) Then Exit Sub  
  54.     
  55.         ' A new DataSet will be generated, and then populated using the readXml() method  
  56.         Dim columns As New DataSet(dg.Name)  
  57.         columns.ReadXml(ComposeGridOptionsFile(dg.Name))  
  58.    
  59.         ' We execute a loop on grid's columns: for each of them, we read and apply the corresponding property's value from the table in DataSet  
  60.         Dim ii As Integer = 0  
  61.         For Each c As DataGridColumn In dg.Columns  
  62.     
  63.             c.DisplayIndex = columns.Tables(0).Rows(ii).Item("DisplayIndex")  
  64.             c.Width = Double.Parse(columns.Tables(0).Rows(ii).Item("Width"))  
  65.             c.Visibility = columns.Tables(0).Rows(ii).Item("Visibility")  
  66.     
  67.             ' The SortDirection case is a particular one: if no sort was set on a column, there will be no corresponding entity  
  68.             ' in the XML file. So, we must manage possible Nothing values  
  69.             Dim sortDirection As Nullable(Of Integer) = Nothing  
  70.             If Not (columns.Tables(0).Rows(ii).Item("SortDirection").Equals(DBNull.Value)) Then sortDirection = Integer.Parse(columns.Tables(0).Rows(ii).Item("SortDirection"))  
  71.    
  72.             ' If SortDirection is specified, we need to execute the actual sort. We compile the list of sort's descriptions through the read value, plus the  
  73.             ' column's SortMemberPath, refreshing the grid's Items.  
  74.             If Not (sortDirection Is NothingThen  
  75.                 c.SortDirection = sortDirection  
  76.                 dg.Items.SortDescriptions.Add(New ComponentModel.SortDescription(c.SortMemberPath, c.SortDirection))  
  77.                 dg.Items.Refresh()  
  78.             End If  
  79.             ii += 1  
  80.         Next  
  81.     End Sub  
  82. End Class  

Use scenario

The typical scenario is represented by entering a window (in case of parameters loading) and from closing it at the end of the program's lifecycle (for saving). Assuming we have a Window named MainWindow, on top of which we create a DataGrid named DataGrid1, we could use Loaded() and Closing() events (if in an event-driven model), respectively for the option's loading and saving. In those event's context, it will be possible to call on the static functions, feeding them the grid's name as a parameter.
  1. Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded  
  2.    'Eseguire qui le operazioni preliminari, tra cui il binding dei dati sulla griglia  
  3.     emDataGridOptions.LoadGridOptions(DataGrid1)  
  4. End Sub  
  5.     
  6. Private Sub MainWindow_Closing(sender As Object, e As ComponentModel.CancelEventArgs) Handles Me.Closing  
  7.     emDataGridOptions.SaveGridOptions(DataGrid1)  
  8. End Sub