Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / C#
Article

Persist ListView settings with serialization

Rate me:
Please Sign up or sign in to vote.
4.73/5 (29 votes)
8 Dec 20023 min read 114.6K   924   49   19
Serialization is a powerful feature of .NET. Here I use it to add functionality to the ListView control.

Sample Image - SaveListView.jpg

Introduction

There are several limitations of the ListView control that comes with .NET. First, there is no way to query the order of the columns if you allow the user to reorder them (set AllowColumnReorder=true). Second, there are no events generated when the user moves a column or changes the width of a column. Somebody else might take the time to write a replacement for this control; I needed something quicker.

In this article, I'll show you how to persist the column order and width settings, by using serialization, binary serialization to be more specific. And you won't believe how easy it is.

You can use the demo source from this article to follow along. If you're using your own code, make sure the ListView control is in details mode (set View=Details) or you'll be confused all the way through. Also make sure you have AllowColumnReorder set to true.

The code

The class ListViewSettings is where all the work is done and there is some funky code going on here, so it's nice to encapsulate that in one place. Unfortunately, the ListView control provides no means of querying the column order once the user has reordered them. The columns are always returned in the order they were created. To get the column order, we have to send a message to the control and ask for them. And to set the order, we have to send another message.

C#
bool ret = SendMessage(listView.Handle, LVM_GETCOLUMN, 
                                             column.Index, ref pcol);
bool ret = SendMessage(listView.Handle, LVM_SETCOLUMN, 
                                             column.Index, ref pcol);

We're also interested in saving and restoring the column widths, so we set up a class that contains all the necessary items:

C#
[Serializable]
public class ListViewColumn
{
    public string header;
    public int width;
    public int order;

    public ListViewColumn( string colHeader, int colWidth, int colOrder )
    {
        header = colHeader;
        width = colWidth;
        order = colOrder;
    }
}

And we mark this class as Serializable with an attribute. That tells the compiler that we are interested in saving this object. You'll need to use the Serializable attribute on every class you plan on saving, even if objects from that class will only ever be created within another class marked as Serializable. In other words, the Serializable attribute is not inherited. So you'll notice that the class that contains all the ListViewColumns is also marked Serializable:

C#
[Serializable]
public class ListViewSettings
{...}

When you serialize objects, you do have some say in how the objects get saved. And sometimes it is necessary to tell the compiler to save objects in a certain way. In this case, there is an ArrayList that contains all the ListViewColumn objects. We want those to be saved as elements, so we can control that through additional attribute tags:

C#
[XmlElement("ListViewColumns", typeof(ListViewColumn))]
public ArrayList listViewCols = new ArrayList();

Now for the fun and easy part:

Since we set up our object to be Serializable, with just a few lines of code we can save it in a binary format:

C#
ListViewSettings listViewSettings = new ListViewSettings( listView );

try
{
    IFormatter formatter = new BinaryFormatter();
    Stream stream = new FileStream("ListViewSettings.config", 
              FileMode.Create, FileAccess.Write, FileShare.None);
    formatter.Serialize(stream, listViewSettings);
    stream.Close();
}
catch {}

And reading it back in to reconstitute our object, is just as easy:

C#
ListViewSettings listViewSettings = null;

try
{
    IFormatter formatter = new BinaryFormatter();
    Stream stream = new FileStream("ListViewSettings.config", 
                  FileMode.Open, FileAccess.Read, FileShare.Read);
    listViewSettings = (ListViewSettings) formatter.Deserialize(stream);
    stream.Close();
    listViewSettings.RestoreFormat( listView );
}
catch {}

Now there is still another limitation of the ListView control...it doesn't generate an event when the user drags or resizes its columns. In the demo application, you control the saving and restoring by clicking buttons. In your own applications, you'll need to place calls to SaveColumnData() and RestoreColumnData() in appropriate places in your code. The form's closing event is one good place for saving the data:

C#
private void MainForm_Closing(object sender, 
                System.ComponentModel.CancelEventArgs e)
{
    try
    {
        if( listView.View == View.Details )
            SaveColumnData();
    }
    catch {}
}

Don't forget to change the connection string in the form_load event to point to a database on your network that has the Northwind database installed.

Conclusion

Serialization can be used for a lot of things, creating persistent objects is just one. But in this case, serialization is the perfect solution for saving and restoring column settings in a ListView. I hope you can make good use of this code in your own projects.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
I'm a software engineer and consultant working in San Diego, California. I began using .NET during the early alpha releases since I worked for a Microsoft subsidiary then, and now I've focused my career on .NET technologies.

There are a lot of .NET sites out there now, but The Code Project is still top of the heap.

Comments and Discussions

 
QuestionSAve the items of my listview as XLM file Pin
alonetmr1-Jul-13 3:27
alonetmr1-Jul-13 3:27 
GeneralMy vote of 5 Pin
Gun Gun Febrianza7-Aug-12 4:05
Gun Gun Febrianza7-Aug-12 4:05 
GeneralDeserialize not working Pin
Jayson Ragasa21-Jan-10 9:57
Jayson Ragasa21-Jan-10 9:57 
GeneralRe: Deserialize not working Pin
Jayson Ragasa21-Jan-10 10:26
Jayson Ragasa21-Jan-10 10:26 
Generala little contribution Pin
Jayson Ragasa21-Jan-10 10:36
Jayson Ragasa21-Jan-10 10:36 
General.NET 2.0 Note Pin
mb1819-Sep-08 12:18
mb1819-Sep-08 12:18 
GeneralFantasitic work Pin
Asghar Panahy4-Oct-07 2:31
Asghar Panahy4-Oct-07 2:31 
GeneralRe: Fantasitic work Pin
john_mcp11-May-08 13:37
john_mcp11-May-08 13:37 
GeneralChanging the serialized objects Pin
werbej8-Jan-07 11:22
werbej8-Jan-07 11:22 
GeneralDynamic Columns Pin
lakhanisa17-May-06 13:21
lakhanisa17-May-06 13:21 
GeneralColumns must be sorted by order to restore correctly Pin
Guido_d31-Aug-05 2:35
Guido_d31-Aug-05 2:35 
GeneralProblem with restoring layout Pin
Colin Parker14-Oct-04 5:05
Colin Parker14-Oct-04 5:05 
GeneralRe: Problem with restoring layout Pin
Colin Parker14-Oct-04 23:33
Colin Parker14-Oct-04 23:33 
GeneralRe: Problem with restoring layout Pin
Wolfgang Schardt19-Oct-04 0:00
Wolfgang Schardt19-Oct-04 0:00 
Generalthis code is not working for large number of columns Pin
mikica197224-May-04 20:37
mikica197224-May-04 20:37 
GeneralRe: this code is not working for large number of columns Pin
Chubby Arse15-Aug-05 1:08
Chubby Arse15-Aug-05 1:08 
GeneralExtending this schema to serialize more objects Pin
Josep L Colom18-Feb-03 3:43
Josep L Colom18-Feb-03 3:43 
GeneralRe: Extending this schema to serialize more objects Pin
Alen Oblak14-Aug-06 9:34
Alen Oblak14-Aug-06 9:34 
A bit late answer, but better than no answer:

First, insert this line somewhere in the listViewSettings class (It's a string holding the unique name of the listview):
public string listViewSettingsName;

Then you need a class in which you have an array of listViewSettings objects and then you serialize that class. That way you can have more listViewSettings objects in one file.

[Serializable]<br />
public class MultiListViewSettings<br />
{<br />
	public ArrayList listViewSettings = new ArrayList();<br />
	<br />
	public void RestoreFormat(string name, ListView listview) <br />
	{<br />
		foreach(ListViewSettings list in this.listViewSettings ) <br />
		{<br />
			if(list.listViewSettingsName == name)<br />
				list.RestoreFormat(listview);<br />
		}<br />
	}<br />
	public void Save(string name, ListView listview) <br />
	{<br />
		ListViewSettings listDelete = null;<br />
		ListViewSettings listInsert = null;<br />
		foreach(ListViewSettings list in this.listViewSettings ) <br />
		{<br />
			if(list.listViewSettingsName == name)<br />
				listDelete = list;<br />
		}<br />
		this.listViewSettings.Remove(listDelete);<br />
		listInsert = new ListViewSettings(listview);<br />
		listInsert.listViewSettingsName = name;<br />
		this.listViewSettings.Add(listInsert);<br />
	}<br />
}


Loading up the settings (application-wide):
multiListViewSettings = new MultiListViewSettings();<br />
IFormatter formatter = new BinaryFormatter();<br />
Stream stream = new FileStream("settings.config", FileMode.Open, FileAccess.Read, FileShare.Read);<br />
multiListViewSettings = (multiListViewSettings) formatter.Deserialize(stream);<br />
stream.Close();


Saving the settings (application-wide):
IFormatter formatter = new BinaryFormatter();<br />
Stream stream = new FileStream(multiListViewSettings , FileMode.Create, FileAccess.Write, FileShare.None);<br />
formatter.Serialize(stream, multiListViewSettings);<br />
stream.Close();


In each form, you must call the function to restore the proper listviews:
multiListViewSettings.Restore("listViewOne", this.listView1);<br />
multiListViewSettings.Restore("listViewTwo", this.listView2);


And in each form you must call the function to save the settings of each listwiev:
multiListViewSettings.Save("listViewOne", this.listView1);<br />
multiListViewSettings.Save("listViewTwo", this.listView2);

And if you have UserControl, don't try to call this in Dispose method, it won't work.
GeneralSome modificatins which you can insert into yours code Pin
Oleksandr Kucherenko15-Dec-02 21:39
Oleksandr Kucherenko15-Dec-02 21:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.