使用事件将UI元素绑定到字段时,如何防止无限递归?

以下似乎是一个相对常见的模式(对我来说,而不是整个社区)将字符串变量绑定到TextBox的内容。

class MyBackEndClass { public event EventHandler DataChanged; string _Data; public string Data { get { return _Data; } set { _Data = value; //Fire the DataChanged event } } } class SomeForm : // Form stuff { MyBackEndClass mbe; TextBox someTextBox; SomeForm() { someTextBox.TextChanged += HandleTextBox(); mbe.DataChanged += HandleData(); } void HandleTextBox(Object sender, EventArgs e) { mbe.Data = ((TextBox)sender).Text; } void HandleData(Object sender, EventArgs e) { someTextBox.Text = ((MyBackEndClass) sender).Data; } } 

问题是更改TextBox会触发后端数据值的更改,从而导致文本框发生更改等。这会永远运行。

有没有更好的设计模式(除了诉诸讨厌的布尔标志)正确处理这种情况?

编辑:要清楚,在实际设计中,后端类用于同步多个表单之间的更改。 因此,我不能直接使用SomeTextBox.Text属性。

Billy3

好的我已经写了一些代码,但你可能不喜欢它:)

 public class DataChangedEventArgs : EventArgs { public string Data { get; set; } public DataChangedEventArgs(string data) { Data = data; } } public delegate void DataChangedEventHander(DataChangedEventArgs e); public class BackEnd { public event DataChangedEventHander OnDataChanged; private string _data; public string Data { get { return _data; } set { _data = value; RaiseOnDataChanged(); } } private static readonly object _sync = new object(); private static BackEnd _instance; public static BackEnd Current { get { lock (_sync) { if (_instance == null) _instance = new BackEnd(); return _instance; } } } private void RaiseOnDataChanged() { if(OnDataChanged != null) OnDataChanged(new DataChangedEventArgs(Data)); } } public class ConsumerControl { public event EventHandler OnTextChanged; private string _text; public string Text { get { return _text; } set { _text = value; if (OnTextChanged != null) OnTextChanged(this, EventArgs.Empty); } } } public class Consumer { public ConsumerControl Control { get; set; } public Consumer() { Control = new ConsumerControl(); BackEnd.Current.OnDataChanged += NotifyConsumer; Control.OnTextChanged += OnTextBoxDataChanged; } private void OnTextBoxDataChanged(object sender, EventArgs e) { NotifyBackEnd(); } private void NotifyConsumer(DataChangedEventArgs e) { Control.Text = e.Data; } private void NotifyBackEnd() { // unsubscribe BackEnd.Current.OnDataChanged -= NotifyConsumer; BackEnd.Current.Data = Control.Text; // subscribe again BackEnd.Current.OnDataChanged += NotifyConsumer; } } public class BackEndTest { public void Run() { var c1 = new Consumer(); var c2 = new Consumer(); c1.Control.Text = "1"; BackEnd.Current.Data = "2"; } } 

主要的idia在这里:

 // unsubscribe BackEnd.Current.OnDataChanged -= NotifyConsumer; BackEnd.Current.Data = Control.Text; // subscribe again BackEnd.Current.OnDataChanged += NotifyConsumer; 

即使我无法复制这个问题,我也知道如何解决它。

目前你实际上有一个DataSetEvent而不是DataChangedEvent

 class MyBackEndClass { public event EventHandler DataChanged; private string data = string.Empty; public string Data { get { return this.data; } set { // Check if data has actually changed if (this.data != value) { this.data = value; //Fire the DataChanged event } } } } 

这应该会停止递归,因为现在你得到TextBoxTextChanged-> DataChanged-> TextBoxChanged – >数据没有改变事件停在这里。

编辑:也许将此代码移动到TextBox中以删除闪烁:
用这个替换你的System.Windows.Forms.TextBox

 class CleverTextBox : TextBox { private string previousText = string.Empty; public CleverTextBox() : base() { // Maybe set the value here, not sure if this is necessary..? this.previousText = base.Text; } public override OnTextChanged(EventArgs e) { // Only raise the event if the text actually changed if (this.previousText != base.Text) { this.previousText = this.Text; base.OnTextChanged(e); } } } 

使用绑定。

 someTestBox.BindingContext.Add(new Binding("Text", mbe, "Data")); 

编辑:我的appologies,它是BindingContext ,它应该工作,如果您将所有表单绑定到后端对象,当一个更新BEO,它将更新附加到它的所有表单(并且它不会递归爆炸。)

您可以在MyBackEndClass的类Data属性的set访问器中检查发件人。如果它的SomeForm – 只是不要引发事件。

我认为你将被困在一个布尔标志,或更好的某种枚举值,你传递到你的HandleTextBox方法。

您可以比较新旧字符串值以查看该值是否已实际更改,并保证更改文本框中显示的值。

它有点脏…但您可以尝试解析之前调用TextChanged的Environment.StackTrace属性。 我认为它比布尔值更脏一些,这让我为线程安全而哭泣。