如何在表格的一个列单元格中输入字母,而在另一列单元格中只输入数字?

Posted

技术标签:

【中文标题】如何在表格的一个列单元格中输入字母,而在另一列单元格中只输入数字?【英文标题】:How to enter letters in one column cell of the table and only numbers in the other column cell? 【发布时间】:2021-12-08 02:32:30 【问题描述】:

我想在 C# 中创建一个表。该表包含两个列标题。一栏标题是产品,另一栏标题是产品价格。 我想通过单击表格中的单元格来输入数据。我只想在产品价格列的单元格中输入数字。 有人知道如何在产品价格下方的单元格中只允许数字吗?

Picture of the table

我的想法: 我的想法是,如果我在产品价格列的单元格中输入一个字符串,我应该使用“替换”使单元格为空。也许可以使用正则表达式,虽然我不知道如何。我的另一个想法是,离开单元格后,如果单元格中有字符串,我必须清空单元格。但不幸的是,我不知道如何实现我的想法。我不知道如何处理这项任务。

我想使用“Windows Forms App (.Net Framework)”来完成我的任务。

【问题讨论】:

docs.microsoft.com/en-us/dotnet/desktop/winforms/controls/… 【参考方案1】:

一个经常被忽视的问题是只允许在单元格中输入数字字符的所有麻烦是……用户仍然可以将有问题的文本“复制/粘贴”到单元格中。所以从技术上讲,即使我们只允许输入数值……这并不能免除我们在用户离开单元格时验证文本是否为有效数字的麻烦。

因此,由于无论如何我们都必须验证文本,所以为此设置了网格CellValidating 事件。当此事件触发时,我们知道用户正在尝试“离开”单元格,因此,在用户离开单元格之前,我们需要检查文本是否为有效数字。否则,网格的DataError 将继续抱怨,直到问题文本被修复。

鉴于此,您可以选择。如果您不关心用户在单元格中输入的值,而只关心用户“离开”单元格时,那么您需要做的就是连接网格CellValidating 事件。如果您想通过只允许在单元格中输入数值来“帮助”用户,则需要做更多的工作。

此外,如果我们连接网格的CellValidating 事件,那么我们可以在坏数据被传递到底层数据源之前捕获它。然而,问题是……“当这种情况发生时我们该怎么办?”我们必须做点什么;如果没有网格DataError 抱怨,我们就不能放手。

有几种方法可以解决这个问题,我在下面使用的方法是,如果文本无效,则会显示一个消息框,说明单元格中的文本无效。此外,违规单元格被着色为浅红色,以帮助用户查看哪个单元格是违规单元格。当用户按下对话框上的“确定”按钮时,有问题的文本将替换为之前的值。基本上,用户在按下Esc 键减去我们警告用户的消息框提示时会遇到同样的情况。

这是您的决定,IMO,如何处理此问题,警告用户,然后返回旧值,然后继续。这样一来,用户在修复有问题的文本之前就不会一直被窃听,但是,在某些情况下,这可能不是所需的行为。

因此,在此示例中,我们希望帮助用户并且只允许将数值输入到单元格中,并且当用户离开单元格时,我们希望确保键入/或粘贴的文本有效。我们在上面讨论的最后一部分......我们将使用网格Cellvaidating 事件专门用于用户离开单元格时。

大图……

从大局的角度来看,我们将在下面做的事情是这样的:首先我们将连接网格EditingControlShowing 事件。此事件是第一步,它将允许代码检查正在编辑的“哪个”单元格。如果编辑的单元格是数字单元格之一,那么我们只需将该单元格转换为名为@9​​87654332@ 的全局TextBox 变量,然后连接该文本框KeyPress 事件。然后我们只需等待用户输入字符。

当用户确实输入了某些内容时,文本框KeyPress 事件将触发。在这种情况下,我们将简单地检查字符是否是有效的数字字符。如果字符不是有效的数字字符,那么我们将“忽略”该字符并继续。

最后,当用户离开单元格时,这将触发网格CellValidating 事件,我们将在其中检查有效的数值。显然,如前所述,我们需要这个事件以防用户将一些违规文本“复制/粘贴”到单元格中。

以下是上述内容的一个小例子。在示例中,网格有四 (4) 列:描述、成本、数量和总计。描述是一个简单的string 单元格,可以包含字母字符和数字。成本单元格是 decimal 值,我们将只允许数值和一个小数位。数量列将是一个int 值,其中只允许使用数值。最后是总计列,它是基础DataTable 中的表达式列,用于计算成本乘以数量。请注意,用户无法编辑总计列。

创建一个新的 winforms 解决方案并将 DataGridView 拖放到表单上,然后按照以下说明进行操作。成品应该看起来像……

首先,我创建了一个名为CurrentCellBeingEdited 的全局变量TextBox。此变量将设置为当前编辑的单元格。这是为了方便而使用的,并不是真正需要的,但是对于这个例子,它使事情变得更清楚了。

TextBox CurrentCellBeingEdited = null;

如果该单元格是数值单元格之一,我们将将此变量分配给用户键入的单元格。然后我们将订阅(连接)TextBoxKeyPress 事件到正确的KeyPress 事件。这一切都在网格EditingControlShowing 事件中完成,可能看起来像……

private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) 
  if (CurrentCellBeingEdited != null) 
    CurrentCellBeingEdited.KeyPress -= new KeyPressEventHandler(DecimalNumbersOnlyCell_KeyPress);
    CurrentCellBeingEdited.KeyPress -= new KeyPressEventHandler(NumbersOnlyCell_KeyPress);
  
  string targetCellColName = dataGridView1.Columns[dataGridView1.CurrentCell.ColumnIndex].Name;
  if (targetCellColName == "Cost" || targetCellColName == "Qty") 
    CurrentCellBeingEdited = (TextBox)e.Control;
    if (targetCellColName == "Cost") 
      CurrentCellBeingEdited.KeyPress += new KeyPressEventHandler(DecimalNumbersOnlyCell_KeyPress);
    
    else 
      // Qty cell
      CurrentCellBeingEdited.KeyPress += new KeyPressEventHandler(NumbersOnlyCell_KeyPress);
    
  

如果需要,此事件只是将TextBox 单元格连接到正确的按键事件。第一个 if 语句……

if (CurrentCellBeingEdited != null) 
  CurrentCellBeingEdited.KeyPress -= new KeyPressEventHandler(DecimalNumbersOnlyCell_KeyPress);
  CurrentCellBeingEdited.KeyPress -= new KeyPressEventHandler(NumbersOnlyCell_KeyPress);

用于取消订阅(取消绑定)任何先前订阅的事件。这可以防止事件在错误的单元格中触发。例子;如果用户选择描述单元格。同样的想法也适用于我们不希望用户为小数点输入句点的 Quantity 单元格。主要的一点是,如果我们不从事件中“取消订阅”文本框,那么它可能会被多次触发,并且可能针对错误的单元格。

取消订阅之前启用的任何事件后,代码只会检查正在编辑的单元格。如果编辑的单元格是 Cost 或 Quantity 列,则代码会将我们的全局变量 CurrentCellBeingEdited 强制转换为已编辑的单元格,然后订阅相应的事件。

接下来,我们需要成本和数量单元格的两个 KeyPress 事件,它们可能看起来像……

// Quantity cell
private void NumbersOnlyCell_KeyPress(object sender, KeyPressEventArgs e) 
  if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar)) 
    e.Handled = true;
  


// Cost cell
private void DecimalNumbersOnlyCell_KeyPress(object sender, KeyPressEventArgs e) 
  if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) && e.KeyChar != '.') 
    e.Handled = true;
  
  if (e.KeyChar == '.' && (sender as TextBox).Text.IndexOf('.') > -1) 
    e.Handled = true;
  

最后,网格CellValidating 事件。请注意,代码允许单元格为“空”。代码检查单元格是成本还是数量列,然后将TryParse 应用于相应的单元格,以验证单元格中的文本是否确实是有效的intdecimal 值。如果TryParse 失败,则单元格将显示为红色,并显示一个消息框,指示无效文本。用户点击消息框上的确定按钮后,编辑被取消。

private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) 
  if (CurrentCellBeingEdited != null) 
    string targetCellColName = dataGridView1.Columns[e.ColumnIndex].Name;
    if (targetCellColName == "Cost" || targetCellColName == "Qty") 
      if (!string.IsNullOrEmpty(CurrentCellBeingEdited.Text))  // <- Allow empty cells
        bool valid = true;
        if (targetCellColName == "Cost") 
          if (!decimal.TryParse(CurrentCellBeingEdited.Text, out decimal value)) 
            valid = false;
          
        
        if (targetCellColName == "Qty") 
          if (!int.TryParse(CurrentCellBeingEdited.Text, out int value2)) 
            valid = false;
          
        
        if (!valid) 
          CurrentCellBeingEdited.BackColor = Color.LightCoral;
          MessageBox.Show("Invalid input - value will revert to previous amount");
          dataGridView1.CancelEdit();
          CurrentCellBeingEdited.BackColor = Color.White;
        
      
    
  

将所有这些放在一起并完成示例,下面的代码使用上面的事件来演示此功能。

TextBox CurrentCellBeingEdited = null;

public Form1() 
  InitializeComponent();
  dataGridView1.CellValidating += new DataGridViewCellValidatingEventHandler(dataGridView1_CellValidating);
  dataGridView1.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler(dataGridView1_EditingControlShowing);



private void Form1_Load(object sender, EventArgs e) 
  DataTable GridTable = GetDT();
  GetData(GridTable);
  dataGridView1.DataSource = GridTable;


private DataTable GetDT() 
  DataTable dt = new DataTable();
  dt.Columns.Add("Description", typeof(string));
  dt.Columns.Add("Cost", typeof(decimal));
  dt.Columns.Add("Qty", typeof(int));
  dt.Columns.Add("Total", typeof(decimal), "Cost * Qty");
  return dt;


private void GetData(DataTable dt) 
  dt.Rows.Add("Product1", 12.49, 2);
  dt.Rows.Add("Product3", 2.33, 3);
  dt.Rows.Add("Product16", 5.00, 12);

我希望这有意义并有所帮助。

【讨论】:

【参考方案2】:
public partial class Form1 : Form

    public Form1()
    
        InitializeComponent();
    

    private void Btn_FillDataGridView_Click(object sender, EventArgs e)
    
        var source = new BindingSource();
        List<Product> list = new List<Product>  new Product("Shoes",50.14), new Product("T-Shirt", 20.55) ;
        source.DataSource = list;

        DtGrdV_Products.DataSource = source;
    


public class Product

    private string _name;
    private double _price;

    [DisplayName("Product")]
    public string Name  get => _name; set => _name = value; 

    [DisplayName("Price of product")]
    public double Price  get => _price; set => _price = value; 

    public Product(string name, double price)
    
        Name = name;
        Price = price;
    

【讨论】:

以上是关于如何在表格的一个列单元格中输入字母,而在另一列单元格中只输入数字?的主要内容,如果未能解决你的问题,请参考以下文章

EXCEL里面怎么自动查看单元格是不是包含在另一列

如何查找单元格中是不是包含部分文本并引用另一列数据

如何在 OpenOffice 中将一列单元格从一张表插入到另一张表的单个单元格中?

excel用啥公式能找出某一单元格中的其中某些数据是不是在另一列中显示?

请问如何从Excel一列单元格中,用公式提取唯一值?

excel如何快速判断一个内容是不是在另一列复杂内容中?