我想知道这个问题有一段时间了,但无法真正找到解决方案。我有两个不同的事件处理程序递归地相互调用。一旦事件A被触发,它就会触发事件B,再次触发事件A,以此类推…
基本上,我希望能够在RichTextBox中选择文本,并在组合框中显示相应的字体大小。当我从组合框中选择不同的字体大小时,我希望它的值应用于所选的文本。
这两个事件是:
1) RichTextBox中文本的选择更改事件:
private void MyRTB_SelectionChanged(object sender, RoutedEventArgs e)
{
//Get the font size of selected text and select the concurrent size from the ComboBox.
}
2) 组合框的选定索引更改事件:
private void CmbFont_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//Apply the chosen font size to the currently selected text of the RichTextBox.
}
确保他们每个人都只"做自己的事",而不会在这样做的过程中解雇另一个事件,最好的解决方案是什么?
有时在代码中更改控件的属性会无意中引发事件。例如,更改ListBox或ComboBox的数据源将触发SelectedIndexChanged
事件。使用标志处理这种情况
private bool _loading;
...
_loading = true;
// Fill the ComboBox or ListView here
_loading = false;
在事件处理程序中执行此
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (_loading) return;
...
}
重构代码,使A调用DoSomethingA()
,B调用DoSomethingB()
。这样,如果你想让A做B的功能,你可以只调用DoSomethingB()
,而不进行任何递归调用。
只需使用bool(可能称为dontFireA)并在调用B 之前将其设置在a中
通知属性(用于启用从WPF到非WPF属性的绑定)使用以下技术:
public object MyProperty
{
get
{
return myField;
}
set
{
if (value != myField)
{
myField = value;
NotifyProperyChanged("MyProperty"); // raise event
}
}
}
if(value!=myField)条件可防止无限递归(stackoverlowexception)。在某些情况下(例如浮点数和不准确的值传输),如果使用(Math.Abs(value-myField)>someConstant)来中断递归。
你能用类似的技巧解决你的问题吗?
如果两个事件都在同一个对象上,或者所有者相互引用,您也可以在每个对象上存储一个标志,例如
private void OnEvent()
{
DoSomething();
}
private void DoSomething()
{
this.IsBusy = true;
// do work
// raise event
if (!other.IsBusy)
RaiseEvent();
}
我要做一个有根据的猜测,你自己并没有引发事件A或事件B;假设事件A是TextBox1.TextChanged事件,事件B是TextBox2.TextChanged的事件,它们有类似于的处理程序
public void Textbox1_TextChanged(object sender, EventArgs e)
{
...
TextBox2.Text = someString;
}
public void Textbox2_TextChanged(object sender, EventArgs e)
{
...
TextBox1.Text = someOtherString;
}
在这种情况下,每个处理程序都将通过更改文本来引发另一个文本框的TextChanged事件,从而导致无限递归。
如果您想同时运行一次和仅运行一次,您可以做的第一件事是标记它们已经在运行(更改另一个文本框的文本会导致该文本框的事件处理程序在同一调用堆栈中运行:
public void Textbox1_TextChanged(object sender, EventArgs e)
{
if(handler1Running) return; //the second time through we exit immediately
handler1Running = true;
...
TextBox2.Text = "Something"; //the other event handler is invoked immediately
handler1Running = false;
}
public void Textbox2_TextChanged(object sender, EventArgs e)
{
if(handler2Running) return; //the second time through we exit immediately
handler2Running = true;
...
TextBox1.Text = "Something Else"; //the other event handler is invoked immediately
handler2Running = false;
}
现在,它将深入到三个层次;1的处理程序调用2的处理程序,后者再次调用1的处理程,后者看到1的处理序已经在运行,并在执行任何加深递归的操作之前退出。如果您从更改TextBox2开始,也是如此。
你可以做的另一件事是确保你没有试图将文本框设置为已经存在的值。从一个字符串引用更改为另一个,即使两个引用都是相同的字符串值,也会触发TextChanged事件。如果递归必须自然地继续,但会达到稳定状态,这实际上是第一件要尝试的事情:
public void Textbox1_TextChanged(object sender, EventArgs e)
{
StringBuilder builder = new StringBuilder();
... //build string
//now, even though the builder's ToString will produce a different reference,
//we're making sure we don't unnecessarily change the text.
if(builder.ToString != TextBox2.Text)
TextBox2.Text = builder.ToString();
}
public void Textbox2_TextChanged(object sender, EventArgs e)
{
StringBuilder builder = new StringBuilder();
... //build string
//now, even though the builder's ToString will produce a different reference,
//we're making sure we don't unnecessarily change the text.
if(builder.ToString != TextBox1.Text)
TextBox1.Text = builder.ToString();
}