The following seems to be a relatively common pattern (to me, not to the community at large) to bind a string variable to the contents of a 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;
}
}
The problem is that changing the TextBox fires the changes the data value in the backend, which causes the textbox to change, etc. That runs forever.
Is there a better design pattern (other than resorting to a nasty boolean flag) that handles this case correctly?
EDIT: To be clear, in the real design the backend class is used to synchronize changes between multiple forms. Therefore I can't just use the SomeTextBox.Text property directly.
Billy3
Even though I can't replicate this problem, I have an idea on how to fix it.
Currently you actually have a DataSetEvent
and not a 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
}
}
}
}
This should stop the recursion, because now you get TextBoxTextChanged->DataChanged->TextBoxChanged ->Data hasn't changed events stop here.
EDIT: Maybe move this code into the TextBox to remove the flicker:
Replace your System.Windows.Forms.TextBox
's with this:
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);
}
}
}
Ok I've wrote some code, but you might not like it :)
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";
}
}
The main idia is here:
// unsubscribe
BackEnd.Current.OnDataChanged -= NotifyConsumer;
BackEnd.Current.Data = Control.Text;
// subscribe again
BackEnd.Current.OnDataChanged += NotifyConsumer;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With