How make a DataGridVewLinkColumn sort with the rest of the DataGridView

1.5k Views Asked by At

I populated a DataGridView with a DataTable as DataSource. This DataSource has a column with comments in it. I hide this column as part of the requirements and added a new DataGridVewLinkColumn that when is clicked the user will be able to see that comment.

My problem is when I sort by clicking on any of the headers from the DataGridView, all the DataGridViewLinkColumn links disappear. I have set the SortMode to Automatic in this LinkColumn but seems that I need to do something else because still as soon as I click on the headers from the other columns in the Grid the links disappear.

Any one knows how can I make sure that when the DataGridView is sorted the link column gets sorted accordingly?

Many thanks

OK I figure it out. The problem is because I used a DataTable as DataSource it was binded to the grid and there is no way to add an extra column to a grid source that is already binded and expect that it will bind with the source. To solve this problem I just modified the data table. Add the extra column in the data table with the strings that will be the links in the DataGridView and populate the DataGridView programatically as recommended in http://msdn.microsoft.com/en-us/library/bxt3k60s(v=vs.90).aspx

1

There are 1 best solutions below

0
On BEST ANSWER

Column Sort Modes in the Windows Forms DataGridView Control

When a DataGridView control containing both bound and unbound columns is sorted, the values in the unbound columns cannot be maintained automatically. To maintain these values, you must implement virtual mode by setting the VirtualMode property to true and handling the CellValueNeeded and CellValuePushed events.

This is a bit complicated, so the simplest solution would be to add an extra column into your DataTable.

I'll leave an example below for future reference.

The points are:

  • VirtualMode should be true.
  • CellValueNeeded should be handled properly to show the specified cell values.
  • ColumnHeaderMouseClick should be handled properly to sort by the unbound columns, and to show sort glyphs.

Note:

  • The DataGridView in this example is read-only to make things simple.

This example form contains:

  • A typed DataSet, that has DataTable1 with columns of ID(string), Comment(string):

    private DataSet1 dataSet1;
    
  • A BindingSource:

    private BindingSource dataTable1BindingSource;
        .DataMember = "DataTable1";
        .DataSource = this.dataSet1;
    
  • A DataGridView:

    private DataGridView dataTable1DataGridView;
        .DataSource = this.dataTable1BindingSource;
        .VirtualMode = true;
        .CellValueNeeded += this.dataTable1DataGridView_CellValueNeeded;
        .ColumnHeaderMouseClick += this.dataTable1DataGridView_ColumnHeaderMouseClick;
        .ReadOnly = true;
        .AllowUserToAddRows = false;
        .AllowUserToDeleteRows = false;
    
  • Its columns:

    private DataGridViewTextBoxColumn iDDataGridViewTextBoxColumn;      // bound column
    private DataGridViewTextBoxColumn commentDataGridViewTextBoxColumn; // bound column
    private DataGridViewLinkColumn linkColumn;                          // unbound column
        .SortMode = DataGridViewColumnSortMode.Automatic;
    

And here goes the code:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    // Hold the link texts, in a dictinary
    // keyed by ID (= unique key in DataTable1), to be bound to each row.
    private SortedDictionary<string, string> _linkTexts
        = new SortedDictionary<string, string>();

    private void Form1_Load(object sender, EventArgs e)
    {
        // Bound data sample
        this.dataSet1.DataTable1.AddDataTable1Row("1", "Comment1");
        this.dataSet1.DataTable1.AddDataTable1Row("2", "Comment2");
        this.dataSet1.DataTable1.AddDataTable1Row("3", "Comment3");

        // Unbound data sample
        this._linkTexts.Add("1", "linkA");
        this._linkTexts.Add("2", "linkC");
        this._linkTexts.Add("3", "linkB");
    }

    // Handles ColumnHeaderMouseClick to do custom sort.
    private void dataTable1DataGridView_ColumnHeaderMouseClick(
        object sender, DataGridViewCellMouseEventArgs e)
    {
        // When the unbound column header is clicked,
        if (e.ColumnIndex == this.linkColumn.Index)
        {
            // Create a new DataView sorted by the link text
            // with toggling the sort order.
            DataView newView;
            switch (this.linkColumn.HeaderCell.SortGlyphDirection)
            {
                case SortOrder.None:
                case SortOrder.Descending:
                    this.linkColumn.HeaderCell.SortGlyphDirection
                        = SortOrder.Ascending;
                    newView = this.dataSet1.DataTable1
                        .OrderBy(row => this._linkTexts[row.ID])
                        .AsDataView();
                    break;

                default:
                    this.linkColumn.HeaderCell.SortGlyphDirection
                        = SortOrder.Descending;
                    newView = this.dataSet1.DataTable1
                        .OrderByDescending(row => this._linkTexts[row.ID])
                        .AsDataView();
                    break;
            }

            // Set it as DataSource.
            this.dataTable1BindingSource.DataSource = newView;

            // Clear sort glyphs on the other column headers.
            foreach (DataGridViewColumn col
                     in this.dataTable1DataGridView.Columns)
            {
                if (col != this.linkColumn)
                    col.HeaderCell.SortGlyphDirection = SortOrder.None;
            }
        }
        // The bound column header is clicked,
        else
        {
            // Sorting has done automatically.
            // Reset the sort glyph on the unbound column.
            this.linkColumn.HeaderCell.SortGlyphDirection = SortOrder.None;
        }
    }

    // Handles CellValueNeeded to show cell values in virtual mode.
    private void dataTable1DataGridView_CellValueNeeded(
        object sender, DataGridViewCellValueEventArgs e)
    {
        // Extract the bound row from the current data view.
        DataSet1.DataTable1Row row
            = (this.dataTable1BindingSource[e.RowIndex] as DataRowView)
              .Row as DataSet1.DataTable1Row;

        // For the unbound column,
        if (e.ColumnIndex == this.linkColumn.Index)
        {
            if (row.IsIDNull())
                e.Value = DBNull.Value;
            else
                // get the value from the dictionary.
                e.Value = this._linkTexts[row.ID];
        }
        // For the bound columns,
        else
        {
            // get the value from the data source.
            string propName = this.dataTable1DataGridView
                              .Columns[e.ColumnIndex].DataPropertyName;
            e.Value = row[propName];
        }
    }
}