Search code examples
c#.netwpfdatagriddatagridcell

WPF How to create custom DataGrid with custom DataGridCells?


I'm looking to create a custom DataGrid such that users can attach notes to each cell using a popup input box. Currently I've created a CustomDataGrid class, inheriting from DataGrid, with a ContextMenu that has an option to add a note. When the user chooses to add a note, I find the selected cell, an input box is opened and returns the response, and I store it in a list of lists of strings, where each list of string represents a row. This doesn't work all the time, however, because sometimes no cell is selected, and I get an error message saying: 'Object reference not set to an instance of an object.'. I'm thinking of creating a CustomDataGridCell class, inheriting from DataGridCell, which has its own ContextMenu and note string. The question is, how would I make all cells in my CustomDataGrid a CustomDataGridCell? Is there a better way to do this?

Here is my current CustomDataGrid class:

public class CustomDataGrid : DataGrid
{
    MenuItem miAddNote;
    List<List<string>> notes;

    public CustomDataGrid()
    {
        notes = new List<List<string>>();

        miAddNote = new MenuItem();
        miAddNote.Click += MiAddNote_Click;
        miAddNote.Header = "Add a note";

        this.ContextMenu = new ContextMenu();
        this.ContextMenu.Items.Add(miAddNote);
    }

    private void MiAddNote_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            int rowIndex = this.SelectedIndex;
            int colIndex = this.SelectedCells[0].Column.DisplayIndex;
            InputBox ib = new InputBox(notes[rowIndex][colIndex]);
            if (ib.ShowDialog() == true)
            {
                notes[rowIndex][colIndex] = ib.Response;
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    protected override void OnLoadingRow(DataGridRowEventArgs e)
    {
        base.OnLoadingRow(e);

        int numColumns = this.Columns.Count;
        List<string> newRow = new List<string>();

        for (int i = 0; i < numColumns; ++i)
        {
            newRow.Add("");
        }
        notes.Add(newRow);
    }
}

Solution

  • The question is, how would I make all cells in my CustomDataGrid a CustomDataGridCell?

    There is no easy way to to this I am afraid. And it is not really necessary to create a custom cell type just to get rid of the exception.

    Is there a better way to do this?

    You should simply check whether there are any selected cells before trying to access any:

    private void MiAddNote_Click(object sender, RoutedEventArgs e)
    {
        int rowIndex = this.SelectedIndex;
        if (rowIndex != -1 && SelectedCells != null && SelectedCells.Count > 0)
        {
            int colIndex = this.SelectedCells[0].Column.DisplayIndex;
            InputBox ib = new InputBox(notes[rowIndex][colIndex]);
            if (ib.ShowDialog() == true)
            {
                notes[rowIndex][colIndex] = ib.Response;
            }
        }
    }