Persisting XamDataGrid Field Widths between Runs of an Application

XamDataGrid has many great features, but as of NetAdvantage for WPF v7.2, it does not yet provide a generic way to save and load user settings.  A common requirement for a grid-centric application is that the columns/fields in the grid retain their widths from one run of the application to the next.  I have created a stopgap solution, in the form of a utility class, that we can use until XamDataGrid has native support for this functionality.

I created a class called XamDataGridFieldWidthManager that you can use to handle the saving, loading, and applying of field widths.  It will take a XamDataGrid like this...

 

...and, upon calling its SaveFieldWidths method, it will generate the following XML...

<?xml version="1.0" encoding="utf-8"?>
<field-layouts>
  <field-layout id="master">
    <field name="ID" width="52" />
    <field name="Column 1" width="77" />
    <field name="Column 2" width="71" />
    <field name="Column 3" width="148" />
    <field name="Column 4" width="168" />
  </field-layout>
  <field-layout id="detail">
    <field name="ParentID" width="258" />
    <field name="Whatever" width="190" />
  </field-layout>
</field-layouts>

You can save that XML somewhere, and next time that you display that XamDataGrid in your program, you pass the XML back to XamDataGridFieldWidthManager so that those persisted field widths are applied to the new fields.

Here is how the demo program uses XamDataGridFieldWidthManager:

public partial class Window1 : Window
{
    const string SETTINGS_FILE = "field-widths.xml";
    readonly XamDataGridFieldWidthManager _widthManager;

    public Window1()
    {
        this.InitializeComponent();

        _widthManager = this.CreateWidthManager();

        // NOTE: You *must* attach a handler to the RequestFieldLayoutID event.
        _widthManager.RequestFieldLayoutID += this.OnWidthManagerRequestFieldLayoutID;
    }

    XamDataGridFieldWidthManager CreateWidthManager()
    {
        StreamReader streamReader = null;
        try
        {
            XmlReader xmlReader = null;

            if (File.Exists(SETTINGS_FILE))
            {
                streamReader = new StreamReader(SETTINGS_FILE);
                xmlReader = XmlReader.Create(streamReader);
            }

            return new XamDataGridFieldWidthManager(this.xamDataGrid, xmlReader);
        }
        finally
        {
            if (streamReader != null)
                streamReader.Dispose();
        }
    }

    void OnWidthManagerRequestFieldLayoutID(object sender, RequestFieldLayoutIDEventArgs e)
    {
        // Provide a unique identifier for each of the FieldLayouts in the XamDataGrid.

        if (e.FieldLayout.Key == this.xamDataGrid.DataSource)
            e.UniqueID = "master";
        else
            e.UniqueID = "detail";
    }

    protected override void OnClosing(CancelEventArgs e)
    {
        base.OnClosing(e);

        if (!e.Cancel)
        {
            // Save the field widths to disk.
            var settings = new XmlWriterSettings { Indent = true };
            using (var xmlWriter = XmlWriter.Create(SETTINGS_FILE, settings))
                _widthManager.SaveFieldWidths(xmlWriter);
        }
    }
}

It is important to note that you must hook the RequestFieldLayoutID event and, in that event handling method, supply a unique identifier for each FieldLayout in the XamDataGrid.  This is necessary because XamDataGridFieldWidthManager must be able to map a <field-layout> XML element to a FieldLayout object, but it has no way to produce the same unique identifier for the same logical FieldLayout  across runs of the application.  Since I have no way to do that in a generic fashion, I consult the class's consumer for that information.

Download the source code and demo project here.  You must have Visual Studio 2008 and NetAdvantage for WPF v7.2 or later installed.