Событие WinForms Validating предотвращает закрытие формы клавишей Escape - PullRequest
13 голосов
/ 26 января 2011

У меня есть простая форма с одним TextBox, плюс кнопки ОК и Отмена.AcceptButton и CancelButton формы установлены правильно, а кнопки OK и Cancel имеют свой DialogResult, установленный на «OK» и «Cancel».

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

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

Конечно, нажав ОК или клавишу Enter, запустится событие Validating, которое я подключил к TextBox.Нажатие кнопки «Отмена» позволяет обойти проверку, что идеально.

Однако нажатие клавиши «Escape» для отмены формы не выполняет то же действие, что и нажатие кнопки «Отмена» - вызывает событие проверки и предотвращаетпользователь не может выйти.

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

Полное отработанное решениеis:

Создайте новое приложение 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 будет , а не делать то же самое - оно запускает событие Validating, тогда как оно должно делать то же самое, что и нажатие кнопки Cancel.

Ответы [ 4 ]

16 голосов
/ 26 января 2011

Да, это неловкая изюминка метода 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);
    }
6 голосов
/ 01 октября 2011

Я только что увидел эту проблему, когда искал решение для того же самого, и переопределение ProcessdialogKey является решением, одобренным MS, до тех пор, пока они не исправят ошибку (Escape должен сделать то же самое, что и нажать кнопку Отмена).Обсуждение этой ошибки также можно найти здесь (просто работает с Visual Basic вместо C #. Ошибка старше 5 лет и, видимо, все еще не исправлена): Ошибка или функция?Кнопка «Отмена» против клавиши Escape Я пытаюсь найти решение C ++.

Изменить, чтобы добавить: Решение по ссылке в C #:

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);
}
1 голос
/ 12 октября 2012

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

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

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

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

Если просто установить для него значение Отключено и не вернуть его к исходному значению, оно будет работать, но если клавиша ESC будет перехвачена, например, вышеупомянутым выпадающим списком, то вы всеВнезапно отключили валидацию и при OK.

Touché!

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

0 голосов
/ 19 февраля 2016

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

buttonOk.Click += (_,__) =>
{
  if(txtName.Text.Length == 3)
  {
    errorProvider1.SetError(txtName, "Wrong Length!");
    DialogResult = DialogResult.None;
  }
  else
  {
    errorProvider1.SetError(txtName, string.Empty);
  }
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...