Почему FormClosing срабатывает дважды при вызове Hide () в модальном диалоговом окне?

Мы создали новую форму, которую показываем через ShowDialog, и добавили к ней кнопку «Отмена». Вот как мы открываем форму от ее родителя:

// _modalForm is a class-level instance of our ModalForm class
var result = _modalForm.ShowDialog(this);
MessageBox.Show(result.ToString());

Вот обработчик события Click кнопки отмены в ModalForm.

private void btnCancel_Click(object sender, EventArgs e)
{
    Close();
}

В нашем событии FormClosing у нас есть этот код (на основе этого ответа).

private void ModalForm_FormClosing(object sender, FormClosingEventArgs e)
{
        e.Cancel = true;
        Hide();
        _parentForm.RefreshData();
}

Удивительно, но когда мы нажимаем кнопку «Отменить» (или используем кнопку «X» в верхней части формы), событие FormClosing возникает дважды. Оба раза CloseReason равно UserClosing.

Я дважды проверил, что InitializeComponent не вызывается дважды и что мы подписываемся на событие только один раз. btnCancel не установлен в свойстве CancelButton для формы. Кроме того, в конструкторе не задано DialogResult. Когда я проверяю возвращаемое значение ShowDialog, оно устанавливается на DialogResult.Cancel.

Изменение btnCancel_Click на DialogResult = DialogResult.Cancel вместо Close() и ничего не делая, кроме _parentForm.Refresh() в событии FormClosing, устраняет проблему, когда событие возникает дважды.

Кто-нибудь знает, почему в этом конкретном сценарии событие FormClosing возникает дважды?


person Jeff B    schedule 16.02.2017    source источник
comment
Вы слишком много помогаете. Диалог уже скрывается, когда вы закрываете его, поэтому вам не нужно вызывать Hide () и не нужно e.Cancel = true. На самом деле вы действительно хотите предотвратить его утилизацию. Никакая помощь не нужна, она уже нужна. Вы можете просто вызвать Show () еще раз, чтобы он снова стал видимым. И наоборот, любое диалоговое окно, которое действительно необходимо полностью закрыть, требует явного вызова Dispose (). Всегда проще всего сделать с помощью оператора using.   -  person Hans Passant    schedule 16.02.2017
comment
@HansPassant Хороший улов. Приведенный выше код представляет собой образец приложения для воспроизведения проблемы. Фактическое приложение, в котором возникла проблема, не использует блок using и вместо этого сохраняет ссылку на форму в переменной уровня класса в родительской форме. Я изменил свой вопрос, убрав блок using.   -  person Jeff B    schedule 16.02.2017


Ответы (2)


Это потому, что скрытие модальной формы приведет к ее закрытию с DialogResult.Cancel в качестве результата диалога. Таким образом, если вы вызовете this.Hide() в событии FormClosing, событие будет инициировано снова.

Представьте, если бы он не закрыл форму, ваше приложение было заблокировано скрытой модальной формой!

Примечание. В ответе описывается причина, по которой событие было инициировано дважды. Но, как описано здесь и другие упомянутые, для модальных форм (которые вы показали с помощью ShowDialog) метод Dispose не будет вызываться и форма существует после закрытия, и вы можете использовать ее свойства для получения некоторых данных или вы можете показать их снова. Таким образом, вам не нужно вызывать метод hide.

Для получения дополнительной информации см. Нужно ли мне удалять форму после закрытия формы?

person Reza Aghaei    schedule 16.02.2017
comment
Также ознакомьтесь с Нужно ли мне удалять форму после закрытия формы? - person Reza Aghaei; 16.02.2017
comment
Ответ описывает причину, по которой событие было поднято дважды. Но, как описано в приведенной выше ссылке и других упомянутых, Для модальных форм (которые вы показали с помощью ShowDialog) метод Dispose не будет вызываться, и форма существует после закрытия, и вы можете использовать его свойства для получения некоторых данных или вы можете покажите это снова. Так что вам не нужно вызывать метод hide. - person Reza Aghaei; 16.02.2017

В указанном вами обходном пути нет необходимости, потому что модальные диалоговые окна не удаляются при закрытии. Они предназначены для хранения своих данных после закрытия (поскольку это требуется для вызывающего модального диалогового окна) и их повторного использования по мере необходимости.

Просто дайте ему нормально закрываться. Модальному диалогу это не повредит :)

Обратите внимание, что это также означает, что в отличие от обычных форм, вы должны удалять модальные диалоги вручную, если они не являются постоянными. В вашем сценарии это, скорее всего, означает, что вы захотите удалить диалоговое окно при удалении родительской формы (это происходит автоматически, если вы добавили диалоговое окно в качестве компонента, но необходимо сделать это вручную, если вы создадите его самостоятельно).

person Luaan    schedule 16.02.2017