Подтвердить что ты не робот

WinForms Проверяющее событие предотвращает закрытие формы клавиши Escape

У меня есть простая форма с одним текстовым полем, плюс кнопки "ОК" и "Отмена" . Форма AcceptButton и CancelButton установлены правильно, а кнопки "ОК" и "Отмена" имеют свой диалог DialogResult для "ОК" и "Отмена" .

Я хочу добавить валидацию в TextBox, которая не позволит пользователю очистить форму, когда проверка не удалась, но которая также позволит им отменить, как обычно.

Свойство CausesValidation True по умолчанию для всех элементов управления, но я изменил его на False на кнопке Cancel.

Конечно, нажав ОК или нажав клавишу Enter, будет запущено событие проверки, которое я подключил к TextBox. Нажатие кнопки "Отмена" обходит "Проверка", которая идеально подходит.

Однако нажатие Escape для отмены формы делает не выполнение так же, как нажатие кнопки "Отмена" - это вызывает событие проверки и предотвращает выход пользователя.

Есть ли способ заставить клавишу Escape выполнять, как предполагалось, то есть не поднимать событие проверки, как если бы была нажата кнопка "Отмена" ?

Полностью обработанное решение:

Создайте новое приложение Windows Forms. Добавить вторую форму в проект.

Вставьте этот код в конструктор Form1 после InitializeComponent():

MessageBox.Show((new Form2()).ShowDialog().ToString());

Это показывает, что DialogResult передан из нашей второй формы.

Вставьте этот код в конструктор Form2 после InitializeComponent():

TextBox txtName = new TextBox();

txtName.Validating +=
    new CancelEventHandler((sender, e) =>
    {
        if (txtName.Text.Length == 3)
        {
            MessageBox.Show("Validation failed.");
            e.Cancel = true;
        }
    });

Button btnOk = new Button
{
    Text = "OK",
    DialogResult = DialogResult.OK
};
Button btnCancel = new Button
{
    Text = "Cancel",
    CausesValidation = false,
    DialogResult = DialogResult.Cancel
};
FlowLayoutPanel panel = new FlowLayoutPanel();
panel.Controls.AddRange(new Control[] 
{
    txtName, btnOk, btnCancel 
});

this.AcceptButton = btnOk;
this.CancelButton = btnCancel;

this.Controls.Add(panel);

В этом упрощенном примере текстовое поле не позволит вам продолжить, если есть 3 символа ввода. Вы можете нажать кнопку "Отмена" или закрыть форму напрямую, даже если присутствуют 3 символа; однако нажатие клавиши Escape будет не делать то же самое - оно запускает событие проверки, тогда как оно должно делать то же самое, что нажатие Cancel.

4b9b3361

Ответ 1

Да, это неудобная причуда метода ValidateChildren. Он не знает, что отмена была задумана. Вставьте этот код, чтобы устранить проблему:

    protected override void OnFormClosing(FormClosingEventArgs e) {
        base.OnFormClosing(e);
        e.Cancel = false;
    }

Чтобы избежать выполнения обработчика события Validate, вызывающего побочные эффекты, например, окна сообщения, добавьте этот оператор в начало метода:

    private void txtName_Validating(object sender, CancelEventArgs e)
    {
        if (this.DialogResult != DialogResult.None) return;
        // etc..
    }

Вставьте этот код в свою форму, чтобы установить набор DialogResult, прежде чем он попытается подтвердить форму:

    protected override bool ProcessDialogKey(Keys keyData) {
        if (keyData == Keys.Escape) this.DialogResult = DialogResult.Cancel;
        return base.ProcessDialogKey(keyData);
    }

Ответ 2

Я просто видел эту проблему, так как я искал решение для нее, и переопределение ProcessdialogKey является одобренным MS решением, пока они не исправят ошибку (Escape должен делать то же самое, что и нажатие Cancel). Обсуждение этой ошибки также найдено здесь (просто работа с Visual Basic вместо С#. Ошибка старше 5 лет и, по-видимому, еще не исправлена): Ошибка или функция? CancelButton vs Escape Key Я пытаюсь выработать решение на С++.

Изменить для добавления: Решение из ссылки в С#:

protected override bool ProcessDialogKey(Keys keyData)
{
    if (keyData == Keys.Escape)
    {
        this.AutoValidate = AutoValidate.Disable;
        cancelButton.PerformClick();
        this.AutoValidate = AutoValidate.Inherit;
        return true;
    }
    return base.ProcessDialogKey(keyData);
}

Ответ 3

На самом деле это вызывает новые проблемы, поскольку он перехватывает escape, который должны использовать другие элементы управления, например. если у вас есть выпадающее поле со списком, нажатие escape должно закрыть выпадающее поле и не выйти из диалогового окна, который будет выполняться выше.

Можно было бы исключить определенные типы управления в событии escape-ключа, но это не очень хорошее решение, это всего лишь вопрос времени, пока другой элемент управления, который использует escape внутри, не будет введен в форму, например. управление распространением, когда режим редактирования должен быть удален при выходе.

ИМО это довольно затормозило, что они запускают проверку на побег. Кто-нибудь знает, какова идея этого, потому что это не ошибка... но это ошибка.

Если бы этот код вызывал base.ProcessDialogKey(keyData) вместо cancelButton.PerformClick, тогда вы были бы ближе к решению, так как кто-то другой мог бы определить, что делать с ключом эвакуации. Но установка AutoValidate на Disabled здесь, а затем возврат его к исходному значению не препятствует проверке, поскольку он, вероятно, просто для того, чтобы просто отправлять события и помещать сообщение в очередь, не использует это значение до тех пор, пока оно не будет установлено к нему оригинальное значение.

Просто установив для параметра "Отключено" и не возвращая его к исходному значению, он будет работать, но если ключ-побег будет перехвачен, например, вышеупомянутое упакованное combobox, то вы внезапно отключили проверку также на OK.

Туше!

У кого-нибудь есть другие яркие идеи о том, как заставить это работать, без необходимости указывать всевозможные виды управления, которые делают и каковы условия, когда им нужно бежать, например. control - это ComboBox и проверьте, не упало ли оно и если это произойдет.

Ответ 4

Ни обработчики ProcessDialogKeys, ни ответы Validating обработчика не работали для меня, возможно потому, что я использую errorProvider вместо MessageBox. Самый простой ответ, который я получил на работу, - это забыть о Validating и просто использовать следующее: -

buttonOk.Click += (_,__) =>
{
  if(txtName.Text.Length == 3)
  {
    errorProvider1.SetError(txtName, "Wrong Length!");
    DialogResult = DialogResult.None;
  }
  else
  {
    errorProvider1.SetError(txtName, string.Empty);
  }
};