2 события зовут друг друга - PullRequest
1 голос
/ 13 марта 2012

Некоторое время я размышлял об этой проблеме, но не смог найти решение.У меня есть 2 разных обработчика событий, вызывающих друг друга рекурсивно.Как только событие A запускается, оно вызывает событие B, которое снова вызывает событие A и т. Д. *

В основном я хочу иметь возможность выделять текст в RichTextBox и показывать соответствующий размер шрифта в комбокоробка.Когда я выбираю другой размер шрифта в ComboBox, я хочу, чтобы его значение применялось к выделенному тексту.

2 события:

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.
}

Каково было бы наилучшее решение, чтобы каждый из них только «делал свое дело» и не запускал при этом другое событие?

Ответы [ 5 ]

1 голос
/ 13 марта 2012

Иногда изменение свойства элемента управления в коде запускает событие непреднамеренно.Изменение источника данных 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;
    ...
}
0 голосов
/ 13 марта 2012

Я собираюсь сделать обоснованное предположение, что вы сами не поднимаете Событие 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();       
}
0 голосов
/ 13 марта 2012

Просто используйте bool (может быть, dontFireA) и установите его в A непосредственно перед вызовом B

0 голосов
/ 13 марта 2012

уведомляющие свойства (используются для включения привязки из WPF к свойствам, не принадлежащим WPF), используют эту технику:

public object MyProperty
{
    get
    {
        return myField;
    }
    set
    {
        if (value != myField)
        {
            myField = value;
            NotifyProperyChanged("MyProperty"); // raise event
        }
    }
}

Условие if (value! = MyField) предотвращает бесконечную рекурсию (stackoverflowexception).В некоторых случаях (например, числа с плавающей запятой и неточные передачи значений) if (Math.Abs ​​(value - myField)> someConstant) используется вместо этого для прерывания рекурсии.

Не могли бы вы применить подобную технику к вашей проблеме?

Если оба события относятся к одному и тому же объекту или у владельцев есть ссылки друг на друга, вы также можете сохранить флаг для каждого, например

private void OnEvent()
{
    DoSomething();
}

private void DoSomething()
{
    this.IsBusy = true;

    // do work

    // raise event
    if (!other.IsBusy)
        RaiseEvent();
}
0 голосов
/ 13 марта 2012

Измените код так, чтобы A звонил DoSomethingA(), а B звонил DoSomethingB(). Таким образом, если вы хотите, чтобы A выполнял функции B, вы можете просто позвонить DoSomethingB() и не делать никаких рекурсивных вызовов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...