0

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

Gokhan
  • 453
  • 4
  • 10
  • At this time, I am not sure why the cell reverts back to read only, but I will look closer. However, I do have to ask… if the posted code is in the grid’s `CellEndEdit` event, why is the code looping through ALL the rows? Only ONE (1) cell has changed. – JohnG Jan 25 '21 at 19:46
  • @JohnG thanx for your attention. All i want,when user select Column A as "Punch Window", column B is readonly=true, so user can not entry anything.. When choose Window Wall, column B readonly=false; user can enter value. Secondly u re right it is not need to loop all cells, i ll fix that.Tahnx. – Gokhan Jan 25 '21 at 19:57
  • Aside from the loop through the rows, I am assuming that you are forgetting about “where” the code is run. If you type some text into the “col_pcs” cell where the combo box is “Window Wall” then “leave” that cell, then the grids `CellEndEdit` is going to fire! It will see the combo box is “Window Wall” and will remove the text you just typed into the cell. `dgv_MaliyetCalismasi.Rows[row].Cells["col_pcs"].Value = null;` I can only guess this may be the problem you are describing. The cell is not read only, but the code in the end edit event is going to set it null when you leave the cell. – JohnG Jan 25 '21 at 20:33

1 Answers1

0

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!

Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
  • First of all, thank you for taking the time to my question.I'm actually a civil engineer and i made a little program for my company in excel application. During the pandemic i decided to learn c# and move my program from excel to desktop application. After your reply i realized that I have learned c# some, but memorized instead of understanding the logic of programming. Your reply is some of lesson to me.It is not "pasta kadar kolay" but your reply is "Kebap gibi:-)". Thanx again. – Gokhan Jan 26 '21 at 18:08