0

I need some help with the DataGridView class in Windows Forms, I don't know if it's just me but I am really struggling with this class programmatically.

You would think that a simple task as adding a Column as a ComboBox to a DataGridView would be a piece of cake, but apparently it's not! The reason I need to do it programmatically is because I need multiple ComboBoxes on each Row in the DataGridView where the selectable drop down items will be dependent on selections in one or more of the others etc...

Here is how I initialize my DataGridView.

private void InitializeDataGridView()
{
    _objectDataGridView.AutoGenerateColumns = false;
    _objectDataGridView.Columns.Add(new DataGridViewTextBoxColumn
                                        {
                                                DataPropertyName = "Id",
                                                HeaderText = "Id",
                                                ValueType = typeof(int)
                                        });
    _objectDataGridView.Columns.Add(new DataGridViewTextBoxColumn
                                        {
                                                DataPropertyName = "Name",
                                                HeaderText = "Name",
                                                ValueType = typeof(string),
                                                AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill
                                        });
    var objectTypeComboBoxColumn = new DataGridViewComboBoxColumn
                                       {
                                               DataPropertyName = "Type",
                                               HeaderText = "Object Type",
                                               ValueType = typeof(ObjectType),
                                               ValueMember = "Id",
                                               DisplayMember = "Name",
                                               DisplayStyle = DataGridViewComboBoxDisplayStyle.ComboBox
                                       };
    _objectDataGridView.Columns.Add(objectTypeComboBoxColumn);
    var containerComboBoxColumn = new DataGridViewComboBoxColumn
                                      {
                                              DataPropertyName = "Container",
                                              HeaderText = "Container",
                                              ValueType = typeof(Container),
                                              ValueMember = "Id",
                                              DisplayMember = "Name",
                                              DisplayStyle = DataGridViewComboBoxDisplayStyle.ComboBox
                                      };
    _objectDataGridView.Columns.Add(containerComboBoxColumn);
}

Up to this point everything is well and good. Now when I click a TreeNodeDecorator (derived from TreeNode) in a TreeView the OnBeforeSelect event fires of which I am listening to. I then use the TreeNodeDecorator.Id to retrieve a list of associated objects, via Fluent NHibernate (ORM).

With the newly retrieved list I want to clear and then fill the _objectDataGridView with new Rows. In the method listening to the OnBeforeSelect event I then try to set the _objectDataGridView.DataSource like this.

private void SetDataSource(IEnumerable<Object> assignedObjects)
{
    var dataTable = new DataTable();
    dataTable.Columns.Add("Id");
    dataTable.Columns.Add("Name");
    dataTable.Columns.Add("Type");
    dataTable.Columns.Add("Container");
    foreach(var assignedObject in assignedObjects)
    {
        dataTable.Rows.Add(assignedObject.Id,
                           assignedObject.Name,
                           assignedObject.ObjectType,
                           assignedObject.Container);
    }
    _objectDataGridView.DataSource = dataTable;
}

But I haven't gotten to set the selectable items for the DataGridViewComboBoxColumns. I could have set a static set of selectable items when I created the DataGridViewComboBoxColumns, but I need those items to be added dynamically and individually per row and in accordance with selected items in other comboboxes on the same row. And I'm really not sure of how to do that.

I guess there is an event I need to listen to, and from there fill the DataGridViewComboBoxColumns.Items or DataGridViewComboBoxColumns.DataSource with the correct items. I then also need to listen to when selections changes in these ComboBoxes and fill / change up the selectable items in related ComboBoxes.

furier
  • 1,934
  • 1
  • 21
  • 39
  • You mean to say objectTypeComboBoxColumn and containerComboBoxColumn will have different set of values for each row?? – Vijay Hulmani Aug 14 '13 at 07:14
  • yes it may have depending on data in the row, also if i choose a different container, the set of selectable items in the objectTypeComboBoxColumn will change to match, or be greyed out if nothing is selected in containerComboBoxColumn – furier Aug 14 '13 at 07:20
  • The only part I don't fully understand (and which seems to be a pretty important part of the process) is where you say "I then use the TreeNodeDecorater.Id to retrieve a list of associated objects, via Fluent NHibernate (ORM)". What are you doing exactly there and why you have to rely on an external library at all to perform these actions? Even have you tried to store easily-trackable/storable properties, like indices? – varocarbas Aug 14 '13 at 07:32
  • I have used FNH throughout the whole solution for DataAccess, and I would like to continue using it here also. I may have to but i don't want to change out my DataAccessLayer because of this. I mention that I use FNH to try and communicate to you guys that i have a list of Objects (List) which i need to map to the DataGridView – furier Aug 14 '13 at 07:37
  • You are free to do what you want; but the problems seems to be there and if you want help perhaps should share more information on this front. In any case, it might be safer to transfer "simpler information" to any external element; for example: indices (just one integer value), instead of the whole columns (lots of values). – varocarbas Aug 14 '13 at 07:58

1 Answers1

0

I ended up adding the Rows to the DataGridView like this:

private void SetDataSource(IEnumerable<Object> assignedObjects, 
                           List<Container> subContainersAssociatedWithSystem)
{
    _objectDataGridView.Rows.Clear();
    foreach (var assignedObject in assignedObjects)
    {
        var newRowIndex = _objectDataGridView.Rows.Add();
        var row = _objectDataGridView.Rows[newRowIndex];
        row.Cells["Id"].Value = assignedObject.Id;
        row.Cells["Name"].Value = assignedObject.Name;
        var containerColumn = (DataGridViewComboBoxCell)row.Cells["Container"];
        containerColumn.DataSource = subContainersAssociatedWithSystem;
        containerColumn.Value = assignedObject.Container.Id;
        var objectTypeColumn = (DataGridViewComboBoxCell)row.Cells["Type"];
        objectTypeColumn.DataSource = new List<ObjectType> {assignedObject.ObjectType};
        objectTypeColumn.Value = assignedObject.ObjectType.Id;
    }
}

And then listen to the EditingControlShowing event like this:

private void InitializeDataGridView()
{
    ...
    _objectDataGridView.EditingControlShowing += (sender, e) =>
    {
        var cb = e.Control as ComboBox;
        if (_objectDataGridView.CurrentCellAddress.X == objectTypeComboBoxColumn.DisplayIndex && cb != null)
        {
            var value = _objectDataGridView[containerComboBoxColumn.DisplayIndex, _objectDataGridView.CurrentCellAddress.Y].Value;
            if(value != null)
            {
                var containerId = (int)value;
                using(var dao = _daoFactory.Create(_daoAdminRole))
                {
                    var container = dao.Get<Container>(containerId);
                    var objectTypes = dao.GetByQueryObject(new ObjectTypeQueryObject {ContainerType = new ContainerType {Id = container.ContainerType.Id}});
                    cb.DataSource = objectTypes;
                }
            }
        }
    };
    ...
}

Now when I click the objectTypeComboBoxColumn the DataSource gets set with the correct items according to the selection in the containerComboBoxColumn.

furier
  • 1,934
  • 1
  • 21
  • 39
  • I got a bug with this solution, when changing the selection in the `containerComboBox`, and after try to change the selection in the `objectTypeComboBox`, everything is none responsive until i select exactly the same option as previously selected or the first item at index 0. – furier Aug 22 '13 at 05:47