【问题标题】:Differentiating between multiple objects attached to the same event handler区分附加到同一事件处理程序的多个对象
【发布时间】:2013-08-17 05:03:10
【问题描述】:

我很难弄清楚如何搜索/表达我想要完成的任务。我正在寻找是否有更安全/更智能的方法来实现以下示例。

假设我有 3 个带有名称的按钮:

btnOne
btnTwo
btn三

每个按钮的目的是在单击时将唯一的字符串写入某个文本框。由于所有三个按钮都执行非常相似的操作,因此为所有三个按钮创建一个事件处理程序似乎是合乎逻辑的,但是区分三个按钮以便正确的文本框可以获得正确的字符串的聪明方法是什么?我不喜欢目前的做法:

伪,伪代码:

private void Clicked(object sender, EventArgs e)
{
    string buttonName = (sender as Button).Name;
    switch(buttonName)
    {
         case "btnOne":
             tbOne.Text = "This text is from button one";
             break;
         case "btnTwo":
             tbTwo.Text = "This is some different text";
             break;
         case "btnThree":
             tbThree.Text = "Button three text";
             break;
    }
}

另一种方法是为每个按钮设置一个单击事件处理程序,然后该处理程序具有一个通用方法,例如:

private void AfterButtonClicked(Textbox tb, string text)
{
    tb.Text = text;
}

【问题讨论】:

  • 这是 WinForms 吗?如果是这样,所有控件都具有object 类型的tag 属性,因此您可以在其中粘贴任何您想要的东西。就像在每个按钮上设置tag 属性一样简单,当单击它时,从中获取值并输出。

标签: c# events button methods event-handling


【解决方案1】:

这是一个如此简单的案例,为了简单起见,我想我会将它们全部放在自己的事件处理程序中。

如果您想举办一个活动,您可以使用以下方法测试sender

private void Clicked(object sender, EventArgs e)
{
    if (sender is btnOne)
        tbOne.Text = "This text is from button one";
    else if (sender is btnTwo)
        tbTwo.Text = "This is some different text";
    else if (sender is btnThree)
         tbThree.Text = "Button three text";
}

至少如果你重命名按钮,你的程序将无法编译,你会来这里修复它。

你现在的样子,如果你将“btnOne”重命名为“btn1”,tbOne 中的文本将无法更新,你可能不会注意到它。

【讨论】:

  • 以上只是一个例子。可能有 100 个按钮都在做一些复杂的事情,相似到足以保证一个事件处理程序,但仍然需要独特的方法,具体取决于单击的按钮。 "is" 运算符看起来肯定是一种更聪明的方法
【解决方案2】:

在您的情况下,重用代码将 some 文本框的 Text 属性设置为 some 字符串。就我个人而言,我没有看到以您演示的方式编写单个事件处理程序的好处为您演示的操作。恕我直言,它使代码复杂化而不是简化它。

最好使用三个单独的处理程序,将三个单独的文本框属性设置为三个单独的字符串。这样您就无需进行任何进一步的比较来找出哪个按钮发送了事件。

根据实际应用程序和处理程序内的代码,此观察可能有效也可能无效。


但是,您可以重构它的一种方法是:

Dictionary<Button, TextBox> buttonTextboxMap = new Dictionary<Button, TextBox> 
{
    {btnOne, tbOne}, 
    {btnTwo, tbTwo}, 
    {btnThree, tbThree}
};

Dictionary<Button, string> buttonStringMap = new Dictionary<Button, string> 
{
    {btnOne, "This text is from button one"},
    {btnTwo, "This is some different text"},
    {btnThree, "Button three text"}
};

void Clicked(object sender, EventArgs e)
{
    Button btn = (Button)sender;

    buttonTextboxMap[btn].Text = buttonStringMap[btn];
}

第二种方法是使用每个按钮的Tag 属性。我喜欢这个,因为它将所有内容都封装在按钮本身中。但是,缺点是只有一个Tag 属性,并且您不能保证它包含适当的信息。 (您也可以将Button 子类化以保存正确的信息,但我想这取决于您所追求的可重用性以及您将使用该特定按钮的次数):

class TextBoxAndString
{
    public TextBox tb {get; set;}
    public String s {get; set;}
}

.ctor() //the form's constructor
{
    btnOne.Tag = new TextBoxAndString {tb = tbOne, s = "This text is from button one"};
    btnTwo.Tag = new TextBoxAndString {tb = tbTwo, s = "This is some different text"};
    btnThree.Tag = new TextBoxAndString {tb = tbThree, s = "Button three text"};   
} 

void Clicked(object sender, EventArgs e)
{
    Button btn = (Button)sender;
    TextBoxAndString tbs = (TextBoxAndString)btn.Tag;

    tbs.tb.Text = tbs.s;
}

【讨论】:

  • 某种天才使用标签属性:)
  • 假设有 100 个按钮,它们都需要做类似的事情,但比文本框和字符串需要更多的唯一性。通过您的解决方案,我可以创建一个包含每个按钮唯一参数的类、一个列表,然后是一个通用事件处理程序,该处理程序查看与按钮对应的类的列表(保留重构)。这也是一个很好的解决方案
  • @Justin 是的,我喜欢 tag 属性(刚刚看到你对这个问题的评论 :))。唯一的缺点是它不是类型安全的,但存储简单的“额外信息”通常不是问题
  • @Doug 当然,尽管我建议使用字典而不是列表以加快访问时间。或者,正如我所演示的,将其放入 tag 属性中,这样您就不必进行额外的查找。同样,这取决于您要做什么。作为另一个示例,您可以在 Tag 属性中存储一个键值,该属性引用 DataTable 中的一行。
  • @Doug 标签属性是object 类型。 Object 是 .NET 中 everything 的父级,因此您可以期待 Dictionary&lt;Control, string&gt; 但它可以被其他任何东西修改以将其更改为 string,这意味着您的转换将失败。有点像没有人使用ArrayList 了。 ArrayList 允许您在其中插入任何内容,因此可能会导致问题,因为您期望的是一种东西,而可能存在另一种类型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多