在表单上我有两个控件,A和B.每个控件在其值发生变化时发送一个事件。表单通过将B设置为某个值来处理A的ValueChanged事件,并通过设置A的值来处理B的ValueChanged。
我是否需要做一些特殊的事情以防止无限循环,其中用户更改A,发送一个导致B更新的ValueChanged事件,现在将其发送给ValueChanged事件,导致A更新......
我知道一些GUI工具包,比如Qt,在基础设施中内置了逻辑,以防止这样的循环。 (参见"输入你的年龄"在Qt4上的Blanchette& Summerfield书的第1章中的示例。)一些较旧的工具包需要程序员定义和管理标志以检测递归。对于WinForms,我无法在任何地方阅读明确的声明。
对于一个具体的例子,假设A和B是NumericUpDown控件,以华氏度和摄氏度显示温度。当用户更改一个时,另一个更新以显示另一个系统中的相应温度。
答案 0 :(得分:2)
根据我的经验,循环通常会结束,因为值会停止实际更改。你的例子属于这种情况。请考虑以下情形:
这通常在属性中实现,方法是在新值与当前值相同时,在setter的顶部放置一个检查,以便不引发已更改的事件。
答案 1 :(得分:1)
我在实现自定义控件时设法阻止此问题的方法是仅在值实际发生更改时引发事件,如下所示。
public string Text
{
get { return _text; }
set
{
if (_text != value)
{
_text = value;
OnTextChanged();
}
}
}
有人可能会争辩说,在触发事件之前不检查值是否实际不同是错误的代码。
我不记得曾经遇到过Windows窗体控件的这些问题,所以我一直在使用的那些必须正确地进行此检查。
答案 2 :(得分:1)
事件不会在您提供的NumericUpDown示例中循环。 NumericUpDown的 ValueChanged事件仅在值更改时被触发,即如果NumericUpDown的值为5,并且在代码中再次将其设置为5,则不会触发任何事件。当您有两个NumericUpDown控件时,此事件的行为会阻止它循环。
现在假设您有两个NumericUpDown控件A& B.
因此,对于 Windows窗体控件,Framework会为您管理,如果您想为自己的类实现此目的,则遵循类似的原则。在值的setter上,检查新值是否与旧值不同。只有它不同才会发生事件。
答案 3 :(得分:0)
我认为你应该在A事件中设置B之前分离B事件。因此,当您在A事件中设置B时,B事件永远不会触发。对于B事件,你应该做同样的事情。你可以打破循环。
抱歉我的英文。我希望它可以帮到你。
答案 4 :(得分:0)
没有任何标准。作为用户,您应该处理引发无用事件的控件,而不应该处理控件。即使对于.NET自己的控件,文档在这方面也缺乏,所以你只需要尝试它。
使用标志来检测递归是可能的,但更好的IMO是将control.Value = newvalue;
更改为if (control.Value != newvalue) control.Value = newvalue;
。
那就是说,如果你自己编写控件,请不要引发无用的事件,请清楚地记录下这个事实。
答案 5 :(得分:0)
为了回答你的问题,我使用下面的代码构建了一个简单的测试。
public partial class Form1 : Form
{
Random r = new Random();
public Form1()
{
InitializeComponent();
}
private void Form1_Shown(object sender, EventArgs e)
{
// This triggers the infinite loop between the two controls
numericUpDown1.Value = 10;
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
int cur = (int)numericUpDown2.Value;
int r1 = r.Next(1, 100);
while (r1 == cur)
r1 = r.Next(1, 100);
Console.WriteLine("Random in NUM1=" + r1);
numericUpDown2.Value = r1;
}
private void numericUpDown2_ValueChanged(object sender, EventArgs e)
{
int cur = (int)numericUpDown1.Value;
int r1 = r.Next(1, 100);
while (r1 == cur)
r1 = r.Next(1, 100);
Console.WriteLine("Random in NUM2=" + r1);
numericUpDown1.Value = r1;
}
}
表单只有两个NumericUpDown初始化为值1和2.
正如您可以在Visual Studio 2013中测试的那样,在您真正设法在每个ValueChanged事件中生成不同的数字的情况下,没有什么可以阻止无限递归。
为了防止无限循环,你可以使用通常的技术。声明全局表单变量
bool changing = false;
在两个事件中添加此代码
try
{
if (changing) return;
changing = true;
.... event code ....
}
finally
{
changing = false;
}