如何从多个其他控件绑定 TextBox

How to Bind TextBox from Multiple Other Controls

我有什么

我有一个包含 3 个 UserControl 的表单,其中包含用于计算目标 UserControl 中的另一个字段的源字段。

UserControl1 (uc1)

UserControl2 (uc2)

UserControl3 (uc3)

用户控件 4 (uc4)

txtCalc 的计算公式如下

txtCalc.Text = (dpEnd.Value - dpStart.Value).TotalDays * txtRate.Text * txtAmount.Text

我想要实现的目标

每当我更改前 3 个 UserControl 中的任何一个值时,txtCalc 中的文本应该更新为计算值。

我试过的

我试过了DataBindings,但它似乎只适用于一个字段。

例如

uc4.txtAmount.DataBindings.Add("Text", uc2.txtRate, "Text", true, DataSourceUpdateMode.Never);

非常感谢您的意见。

在根据需要绑定日期选择器和文本框时,您可能需要考虑一些事项。此外,您还可以通过多种方式执行此操作。

一个可能的问题是 UI 每个“控件”(日期选择器和文本框)分布在四 (4) 个不同的 UserControls. 这不是一个大问题,大约您唯一需要确保的是每个 UserControls 中的每个“控件”(DTP 和文本框)都“暴露”给主窗体。

换句话说,如果您只是将 TextBox 放在 UserControl 中,然后将 UserControl 放在主窗体上……那么主窗体将无法访问TextBox ……除非它是“暴露的”。这可以通过简单地将 TextBoxes Modifier 属性 设置为 Public. 来解决,然后主窗体将可以访问 UserControl 中的文本框。

因此,第一步是确保我们要在主窗体中访问的每个 UserControls 中的每个 UI“控件”都有其 Modifier 属性 设置为 Public. 这样做的原因是您希望能够从主窗体访问这些控件中的数据。除了我们想从主窗体订阅这些控件“事件”这一事实之外。

UserControl 放置到窗体后,我们将在主窗体中执行的操作是订阅 UI 控件(DTP 或 TextBox)的 TextChanged 事件。然后我们可以在其中一个控件文本发生变化时“捕获”……我们可以在主窗体中捕获这种变化。

另一个可能的问题是代码如何从两个 DateTimePickers. 计算“总天数”数量 DateTimePickers. 当前代码看起来像...

(dpEnd.Value - dpStart.Value).TotalDays

这会起作用,但是,如果您调试代码并仔细查看计算结果,您可能会注意到,如果日期 1 是 1/25/2021,而日期 2 是 1/26/2021……那么您应用上面的代码……您很有可能会得到类似 0.933 的结果……当转换为 int 时,它显然会变成 0,这不是我们所期望的。这样做的原因是因为当您添加或减去两个 DateTime 对象时……计算包括 DateTime 对象的时间部分。

因此,要获得正确的 int 值……您需要“限定”您只需要两个日期之间的“日期”差异。幸运的是 DateTime 对象有一个 Date 属性 可以用来忽略时间部分。因此,只需要一个小的改变就可以解决这个问题,可能看起来像……

(dpEnd.Value.Date - dpStart.Value.Date).TotalDays

正如评论中所建议的,使用Events可能是最容易实现的,也不难理解。基本上,我们将订阅 (wire-up) 每个 UserControls 中的每个控件到 SAME 事件。在该事件中,我们将“更新”计算值。

通常,您会将每个控件连接到它自己的事件,但是,由于您想在控件发生任何更改时简单地“更新”单个文本框,我们可以简化它并创建一个方法来“更新” ” 任何其他控件更改时的计算文本框。我希望这是有道理的。

为了提供帮助,我强烈建议您(在不久的将来)也这样做……正确命名您的变量。将控件命名为 uc1uc2uc3uc4 很好……不是一个好主意。你可以做到,但是很难说出控件是“什么”。看着名字......没有更多的研究,我不知道“哪个”控件有“Rate”文本框。将您的变量命名为有意义的名称以避免任何歧义。在下面的示例中,对于 UserControls,我将它们命名为……UC_StartEndDateUC_Rate 等……

另一个可能的问题是,由于您要执行“计算”,因此您需要将 TextBoxes 中的 string 值解析为 int 值。换句话说……代码……

txtRate.Text * txtAmount.Text

可能会正常工作而不会出现错误,但是我相信它不会给您想要的结果,因为“*”乘数的两边都是“TEXT/string”值,我们需要解析这些值string 值到 int 值进行任何数学计算。

注意 int.TryParse(UC_Rate.txtRate.Text, out int rate); 行代码通常会包含在 if 语句中,因为如果解析成功它将 return true 并且 false如果失败。如果解析失败,那么 out 变量 rate 将被设置为零 (0),这对我来说没问题……如果失败,则使用零 (0) 作为值。您可能想做些不同的事情。

private void UpdateUC_Total() {
  int tdays = (int)(UC_StartEndDate.dpEnd.Value.Date - UC_StartEndDate.dpStart.Value.Date).TotalDays;
  int.TryParse(UC_Rate.txtRate.Text, out int rate);
  int.TryParse(UC_Amount.txtAmount.Text, out int amt);
  int total = tdays * rate * amt;
  UC_Calculated.txtCalc.Text = total.ToString();
}

现在我们所要做的就是订阅 (wire-up) 单个 UI 控件 TextChanged 事件。如前所述,由于我们希望所有控件都执行相同的计算,这简化了事情,我们可以让所有控件子族到同一个事件。这个事件将简单地调用我们上面的方法,可能看起来像……

private void UC_ValueChanged(object sender, EventArgs e) {
  UpdateUC_Total();
}

在表单 Load 事件中,我们可以订阅每个控件中的控件UserControls 到我们上面的事件,它可能看起来像……

private void Form1_Load(object sender, EventArgs e) {
  UC_StartEndDate.dpStart.TextChanged += UC_ValueChanged;
  UC_StartEndDate.dpEnd.TextChanged += UC_ValueChanged;
  UC_Rate.txtRate.TextChanged += UC_ValueChanged;
  UC_Amount.txtAmount.TextChanged += UC_ValueChanged;
}

差不多就这些了。如果任何日期选择器或费率或金额文本框发生变化,则计算出的文本框文本将自动“更新”。您可能需要“离开”控件才能看到更新后的值。


另一种方法是使用每个控件的 DataBindings 属性 将每个控件“绑定”到某物。目前你有代码…

uc4.txtAmount.DataBindings.Add("Text", uc2.txtRate, "Text", true, DataSourceUpdateMode.Never);

这里的问题是数据绑定正在寻找某种类型的 DataSource,例如 DataTableList<T>,或者在下面的示例中是 Class目的。代码“似乎”将 uc2.txtRate 用作 DataSource,这可能不起作用,因为 txtRateTextBox 而不一定是 DataSource.

这可能会很棘手,为了简单起见,我通常会简单地“创建”一个 DataSource 以便更轻松地设置控件 DataBindings 以按我的意愿工作。下面的代码显示了如何使用当前示例执行此“数据绑定”。请记住,这会起作用,但是,它可能会为您带来更多的工作。

所以第 1 步是“创建”一个简单的 DataSource 我们可以用于所有控件(DTP 和文本框)。在这种情况下,我将实现一个具有我们需要的属性的 Class。然后,我们将只实例化这些对象中的一个,然后在设置每个控件 DataBindings 属性 时将该对象用作 DataSource。这个 UC_Helper class 可能看起来像……

public class UC_Helper {
  public DateTime DP_Start { get; set; }
  public DateTime DP_End { get; set; }
  public string Rate { get; set; }
  public string Amount { get; set; }
  public int CalcAmount {
    get {
      int tdays = (int)(DP_End.Date - DP_Start.Date).TotalDays;
      int.TryParse(Rate, out int rate);
      int.TryParse(Amount, out int amt);
      return  tdays * rate * amt;
    }
  }
}

我们将能够使用我们的用户控件“controls”中的值实例化单个 UC_Helper 对象,然后简单地将每个控件的 DataBinding 属性 设置为“指向”实例化 UC_Helper 对象中的正确 属性。

因此在表单加载事件中,您将实例化一个新的 UC_Helper 对象,然后将每个 UserControls 中的每个控件“绑定”到我们的 UC_Helper 对象。 Load 事件形式中的这段代码可能类似于……

UC_Helper ControlHelper;

public Form1() {
  InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e) {
  ControlHelper = new UC_Helper {
    DP_Start = UC_StartEndDate.dpStart.Value.Date,
    DP_End = UC_StartEndDate.dpEnd.Value.Date,
    Rate = UC_Rate.txtRate.Text,
    Amount = UC_Amount.txtAmount.Text
  };
  UC_Amount.txtAmount.DataBindings.Add("Text", ControlHelper, "Amount");
  UC_Rate.txtRate.DataBindings.Add("Text", ControlHelper, "Rate");
  UC_Calculated.txtCalc.DataBindings.Add("Text", ControlHelper, "CalcAmount");
  UC_StartEndDate.dpStart.DataBindings.Add("Text", ControlHelper, "DP_Start");
  UC_StartEndDate.dpEnd.DataBindings.Add("Text", ControlHelper, "DP_End");
}

很抱歉post。我希望这会有所帮助,我建议您选择自己的毒药来使用哪种方法。我倾向于支持事件方法,但是如果有一个好的 DataSource 可用,我可能会使用数据绑定。祝你好运。