Следующее, кажется, относительно общий шаблон (мне, не сообществу в целом) для привязки строковой переменной с содержанием 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. Текстовое свойство непосредственно.
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";
}
}
Основная идея здесь:
// unsubscribe
BackEnd.Current.OnDataChanged -= NotifyConsumer;
BackEnd.Current.Data = Control.Text;
// subscribe again
BackEnd.Current.OnDataChanged += NotifyConsumer;
Использовать привязки.
someTestBox.BindingContext.Add(new Binding("Text", mbe, "Data"));
Редактировать: Приношу свои извинения, это BindingContext
, и он должен работать, если вы привяжете все свои формы к внутреннему объекту, когда кто-то обновляет BEO, он обновляет все прикрепленные к нему формы (и он не взорвется рекурсивно.)
Вы можете проверить отправителя внутри set accessor свойства Data класса MyBackEndClass Если его SomeForm - просто не вызывайте событие.
Несмотря на то, что я не могу воспроизвести эту проблему, у меня есть идея, как ее исправить.
В настоящее время у вас есть 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);
}
}
}
Это немного грязно ... но вы можете попробовать проанализировать свойство Environment.StackTrace для предыдущего вызова TextChanged. Я думаю, что это немного менее грязно, чем логическое значение, которое, как мне кажется, требует безопасности потоков.
Я думаю, вы застрянете с логическим флагом или, еще лучше, каким-то значением перечисления, которое вы передаете в свой метод HandleTextBox.
Вы можете сравнить старые и новые строковые значения, чтобы увидеть, действительно ли значение изменилось, и гарантирует ли изменение значения, отображаемого в текстовом поле.