Search code examples
c#winformseventsdatagridviewselecteditemchanged

DataGrdiview ComboBox change result


My project has a DataGridView with Combobox columns and this column has two item as "Punch Window" and "Window Wall" as you see in picture.

I am using dataGridView1_CellEndEdit event and these are my codes

for (int i = 0; i < dgv_MaliyetCalismasi.Rows.Count; i++)
     {
       if (dgv_MaliyetCalismasi.Rows[i].Cells["col_numberofProduct"].Value != null)
         {
         if (dgv_MaliyetCalismasi.Rows[i].Cells["col_numberofProduct"].Value.ToString() == "PUNCH WINDOW")
                   {
                        dgv_MaliyetCalismasi.Rows[i].Cells["col_pcs"].Value = "No entry";
                        dgv_MaliyetCalismasi.Rows[i].Cells["col_pcs"].ReadOnly = true;
                   }
                   else
                   {
                        dgv_MaliyetCalismasi.Rows[i].Cells["col_pcs"].Value = null;
                        dgv_MaliyetCalismasi.Rows[i].Cells["col_pcs"].ReadOnly = false;
                   }
          }
     }

When i choose Punch Window(Column A) then another column(Column B) return “No entry”. When i choose column A "Window Wall" Column B returns null .everything is ok until here.

My problem is while the Column A selected "Window Wall" item, i try to enter some data in Column B then dataGridView1_CellEndEdit event begins and makes Column B null again. How can i prevent that. With Window Wall selected, i want to handle cellandedit event or want to Column B cells act independent. I want user can enter data in the Column B cells.

Thanx in advance.enter image description here


Solution

  • I see quite often that people intertwine their model with how they display it (the view). You should separate model from view.

    If you separate your data from how the data is displayed and later you decide to change how you display your model, for instance from Forms to WPF, changes will not be in your model. If you decide to change your model, for instance, change from XML serialization to a database, your View won't have to change.

    Separation of model and view also makes unit testing your model easier: you don't need to start a Forms program to see whether you handle your model correctly.

    My Turkish is a bit rusty, but it seems to me that you want to display a sequence of MaliyetCalismasi, where every MaliyetCalismasi has at least a Number, a ProductType and a Pcs, whatever that may be.

    At first glance, it seems that there is a relation between ProductType and Pcs. It seems that if ProductType equals "Punch Window", then Pcs should be "No Entry" and if ProductType equals "Window Wall", then Pcs equals null.

    Apparently the above is not valid: it is possible to change the Pcs to other values than "No Entry" and null. You didn't specify the value of the ComboBox if the operator types a different value in Pcs: show empty ComboBox? show the original value?

    You should decide: did you make ProductType only because I want to be able to do the ComboBox selection in my DataGridView, or did you make it because it is part of the model: if you wouldn't have to display the data in a DataGridView, would you still have a 'ProductType`?

    Model

    I'm not sure if ProductType can have more than these two values. Let's assume not. If it can have more values, you should change the type.

    enum ProductType
    {
        PunchWindow,
        WindowWall,
        ...          // other Values?
    }
    
    class MaliyetCalismasi
    {
        public int Number {get; set;}
        public string Pcs {get; set;}
    
        // TODO: ProductType
    }
    

    We know what should happen with property Pcs if someone sets ProductType to ProductType.PunchWindow. But should anything happen to ProductType if someone changes Pcs?

    private ProductType productType = ProductType.WindowWall;
    
    public ProductType ProductType
    {
        get => this.productType;
        set
        {
            if (this.ProductType != value)
            {
                this.productType = value;
                this.OnProductTypeChanged();
            }
        }
    }
    
    private void OnProductTypeChanged()
    {
        const string pcsPuncWhindowValue = "No Entry";
    
        if  (this.ProductType == ProductType.PunchWindow}
        {
            this.Pcs = pcsPunchWindowValue;
        }
        else
        {
            this.Pcs = null;
        } 
    }
    

    What do you want with ProductType if Pcs is changed? Nothing? or a third value?

    private string pcs = null;
    public string Pcs
    {
        get => this.pcs
        set
        {
            if (this.Pcs != value)
            {
                this.pcs = value;
                this.OnPcsChanged();
            }
        }
    }
    

    By the way, did you see that because I created On...Changed methods, that implementation of PropertyChanged events will be fairly easy.

    Unit testing this class is minimal work.

    It is easy to see, that if in future you decide to add a new ProductType value "ManualSelected", meaning the value that ProductType gets if someone changes the Pcs, then changes will be minimal.

    Display MaliyetCalismasi

    People tend to edit the cells in a DataGridView directly. If you want to do that, think again. If you still think it is needed, do it. Using a DataSource is way easier.

    // Create the DataGridView and the add the columns
    DataGridView dataGridView1 = new DataGridView();
    DataGridViewColumn columnNumber = new DataGridViewColumn();
    DataGridViewColumn columnPcs = new DataGridViewColumn();
    DataGridViewComboBoxColumn columnProductType = new DataGridViewComboBoxColumn();
    
    // which properties should these column show?
    columnNumber.DataPropertyName = nameof(MaliyetCalismasi.Number);
    columnPcs.DataPropertyName = nameof(MaliyetCalismasi.Pcs);
    columnProductType.DataPropertyName = nameof(MaliyetCalismasi.ProductType);
    

    Usually the above is done using visual studio designer. You will also need to do something special to fill the ComboBox. That is not part of this quesiont.

    Now to do a Display only, all you need to do is assign the data to the DataSource of the DataGridView:

    List<MaliyetCalismasi> fetchedMaliyetCalismasi = this.FetchMaliyetCalismasi();
    this.dataGridView1.DataSource = fetchedMaliyetCalismasi;
    

    This will be Display only: if the operator changes the displayed data: changes cells, adds or removes rows, the original data is not updated.

    If you want updated data, you need to put the data in an object that implements IBindingList, like BindingList (obvious name):

    this.dataGridView.DataSource = new BindingList<MaliyetCalismasi>
        (fetchedMaliyetCalismasi);
    

    And presto! Every edited cell is automatically updated in the BindingList, even if Rows are added or removed. Even if operator sorts the displayed row, or rearranges the columns. This is, because you separated your view from your model: the display is changed, your model not.

    Good practice of separation of your Data from how it is displayed, will lead to the following procedures:

    BindingList<MaliyetCalismasi> MaliyetCalismasi
    {
        get => (BindingList<MaliyetCalismasi>)this.DataGridView1.DataSource;
        set => this.DataGridView1.DataSource = value;
    }
    
    IEnumerable<MaliyetCalismasi> FetchMaliyetCalismasi()
    {
        // TODO implement, fetch from database, or Json, or internet, or whatever
    }
    
    void InitDataGridView
    {
        this.MaliyetCalismasi = new BindingList<MaliyetCalismasi>(
            this.FetchMaliyetCalismasi().ToList());
    }
    

    And if you want to do something with Selected Rows:

    MaliyetCalismasi CurrentMaliyetCalismasi => 
        this.DataGridView1.CurrentRow?.DataBoundItem as MaliyetCalismasi;
    
    IEnumerable<MaliyetCalismasi> SelectedMaliyetCalismasi =>
        this.DataGridView1.SelectedRows
            .Select(row => row.DataBoundItem)
            .Cast<MaliyetCalismasi>();
    

    You see, that because you separated your model from the way that it is displayed, handling the display of the data consists of mostly one-liner methods!

    Pasta kadar kolay!