DataGridview ComboBox 更改结果
Posted
技术标签:
【中文标题】DataGridview ComboBox 更改结果【英文标题】:DataGrdiview ComboBox change result 【发布时间】:2021-04-29 14:45:49 【问题描述】:我的项目有一个带有 Combobox 列的 DataGridView,并且该列有两个项目,如您在图片中看到的“Punch Window”和“Window Wall”。
我正在使用 dataGridView1_CellEndEdit 事件,这些是我的代码
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;
当我选择打孔窗口(A 列)时,另一列(B 列)返回“无条目”。当我选择 A 列“窗墙”B 列返回 null 时,一切正常,直到这里。
我的问题是当 A 列选择“窗口墙”项时,我尝试在 B 列中输入一些数据,然后 dataGridView1_CellEndEdit 事件开始并使 B 列再次为空。 我怎样才能防止这种情况。选择 Window Wall 后,我想处理 cellandedit 事件或希望 B 列单元独立运行。我希望用户可以在 B 列单元格中输入数据。
提前致谢。enter image description here
【问题讨论】:
此时,我不确定为什么单元格会恢复为只读,但我会仔细观察。但是,我不得不问……如果发布的代码在网格的CellEndEdit
事件中,为什么代码会循环遍历所有行?只有一 (1) 个单元格发生了变化。
@JohnG 感谢您的关注。所有我想要的,当用户选择a列a为“punch窗口”时,列b是readOnly = true,因此用户无法输入任何东西..选择窗口墙时,列b读取= false;用户可以输入值。其次,你是对的,它不需要循环所有单元格,我会修复它。Tahnx。
除了遍历行之外,我假设您忘记了代码运行的“位置”。如果您在组合框为“Window Wall”的“col_pcs”单元格中键入一些文本,然后“离开”该单元格,那么网格CellEndEdit
将会触发!它会看到组合框是“Window Wall”,并将删除您刚刚输入到单元格中的文本。 dgv_MaliyetCalismasi.Rows[row].Cells["col_pcs"].Value = null;
我只能猜测这可能是您描述的问题。该单元格不是只读的,但是当您离开该单元格时,结束编辑事件中的代码会将其设置为空。
【参考方案1】:
我经常看到人们将他们的模型与他们的显示方式(视图)交织在一起。 您应该将模型与视图分开。
如果您将数据与数据的显示方式分开,然后您决定更改模型的显示方式,例如从 Forms 更改为 WPF,则更改不会出现在您的模型中。如果您决定更改模型,例如从 XML 序列化更改为数据库,则您的视图不必更改。
模型和视图的分离还使您的模型更容易进行单元测试:您无需启动 Forms 程序即可查看您是否正确处理了您的模型。
我的土耳其语有点生疏,但在我看来,您想显示MaliyetCalismasi
的序列,其中每个MaliyetCalismasi
至少有一个Number
、一个ProductType
和一个Pcs
,不管是什么。
乍一看,ProductType 和 Pcs 之间似乎有关系。看来,如果 ProductType 等于“Punch Window”,那么 Pcs 应该是“No Entry”,如果 ProductType 等于“Window Wall”,那么 Pcs 就等于 null。
显然上述无效:可以将 Pcs 更改为“No Entry”和 null 以外的其他值。如果操作员在 Pcs 中键入不同的值,您没有指定 ComboBox 的值:显示空 ComboBox?显示原值?
您应该决定:您是否只因为我希望能够在我的 DataGridView 中进行 ComboBox 选择而创建 ProductType
,还是因为它是模型的一部分而创建它:如果您不必显示DataGridView 中的数据,你还会有一个“ProductType”吗?
型号
我不确定 ProductType 是否可以超过这两个值。我们假设不是。如果它可以有更多的值,你应该改变类型。
enum ProductType
PunchWindow,
WindowWall,
... // other Values?
class MaliyetCalismasi
public int Number get; set;
public string Pcs get; set;
// TODO: ProductType
如果有人将 ProductType 设置为 ProductType.PunchWindow,我们知道属性 Pcs 会发生什么。但是如果有人更改 Pcs,ProductType 会发生什么变化吗?
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;
如果更改了 Pcs,您希望 ProductType 做什么?没有?还是第三个值?
private string pcs = null;
public string Pcs
get => this.pcs
set
if (this.Pcs != value)
this.pcs = value;
this.OnPcsChanged();
顺便说一句,你有没有看到,因为我创建了 On...Changed 方法,PropertyChanged 事件的实现将相当容易。
对这个类进行单元测试是最少的工作。
很容易看出,如果将来您决定添加一个新的 ProductType 值“ManualSelected”,这意味着如果有人更改 Pcs,ProductType 获得的值,那么更改将是最小的。
显示 MaliyetCalismasi
人们倾向于直接编辑 DataGridView 中的单元格。如果您想这样做,请三思。如果你仍然认为它是必要的,那就去做吧。使用数据源要容易得多。
// 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);
以上通常是使用 Visual Studio Designer 完成的。您还需要做一些特殊的事情来填充 ComboBox。这不是这个问题的一部分。
现在只做一个Display,你需要做的就是将数据分配给DataGridView的DataSource:
List<MaliyetCalismasi> fetchedMaliyetCalismasi = this.FetchMaliyetCalismasi();
this.dataGridView1.DataSource = fetchedMaliyetCalismasi;
这将是仅显示:如果操作员更改显示的数据:更改单元格、添加或删除行,则不会更新原始数据。
如果你想要更新数据,你需要把数据放在一个实现了IBindingList的对象中,比如BindingList(名字很明显):
this.dataGridView.DataSource = new BindingList<MaliyetCalismasi>
(fetchedMaliyetCalismasi);
然后转眼!每个已编辑的单元格都会在 BindingList 中自动更新,即使添加或删除行也是如此。即使操作员对显示的行进行排序,或重新排列列。这是因为您将视图与模型分开:显示已更改,而您的模型没有。
将数据与其显示方式分开的良好做法将导致以下程序:
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());
如果你想对选定的行做一些事情:
MaliyetCalismasi CurrentMaliyetCalismasi =>
this.DataGridView1.CurrentRow?.DataBoundItem as MaliyetCalismasi;
IEnumerable<MaliyetCalismasi> SelectedMaliyetCalismasi =>
this.DataGridView1.SelectedRows
.Select(row => row.DataBoundItem)
.Cast<MaliyetCalismasi>();
您知道,因为您将模型与显示方式分开,所以处理数据的显示主要由单行方法组成!
意大利面kadar kolay!
【讨论】:
首先,感谢您抽出宝贵的时间来回答我的问题。我实际上是一名土木工程师,我为我的公司制作了一个小程序,用于 Excel 应用程序。在大流行期间,我决定学习 c# 并将我的程序从 excel 转移到桌面应用程序。在你的回复之后,我意识到我已经学习了一些 c#,但是记住而不是理解编程的逻辑。您的回复对我来说是一些教训。不是“pasta kadar kolay”,而是您的回复是“Kebap gibi:-)”。再次感谢。以上是关于DataGridview ComboBox 更改结果的主要内容,如果未能解决你的问题,请参考以下文章
DataGridView ComboBox Column:从下拉列表中选择后更改单元格值?
使用 DataGridView Combobox 的 SelectionChangeCommitted 事件获取新值或索引