Tuesday, March 25, 2008

Особенности TextBox.MaxLength

Новая рубрика: «Знаете ли вы, что?»

Скорее всего, с этим интересным моментом из жизни .NET Framework уже многие сталкивались, но для тех, кто еще не успел, пишу этот пост.

Итак, проблема: нужно не дать пользователю ввести в какое-нибудь поле ввода на странице больше символов, чем определено в базе данных, иначе вполне очевидно вылетит database exception. Имеется в виду, конечно же, случай, когда мы используем nvarchar(xx) и различные вариации на тему.

Глобально решать эту проблему можно двумя путями: не дать пользователю ввести больше символов, чем нужно, или просто урезать введенную строку (на сервере или на клиенте). Второй вариант является не очень красивым, так как пользователь ожидает, что все введенные им данные будут сохранены приложением, а они будут сохранены в урезанном виде. Кроме того, если пытаться урезать данные на сервере, то пользователь может запросто ввести больше 100 Мб текста в поле ввода, что спровоцирует exception еще на подступах к приложению (IIS 6 по умолчанию имеет ограничение в 4 Мб на передачу запросов/ответов, решение этой проблемы хорошо описано в интернете на форумах). Так что более правильным вариантом является явное ограничение размера поля на клиенте.

Казалось бы, что может быть проще. В TextBox'е есть замечательное свойство MaxLength, которое нам и нужно установить для всех наших полей ввода. В сгенерированном HTML-элементе это свойство просто становится атрибутом maxLength. Проставляем, запускаем, пробуем и радуемся. Все работает просто замечательно :). Устанавливаем баги в баг-трекинг системе и забываем о проблеме. Не тут-то было. Через время часть багов возвращается с feedback'ами. Для некоторых полей свойство почему-то не срабатывает. Перепроверяем и сразу же понимаем, в чем проблема – не срабатывают многострочные текстбоксы, которые транслируются в <textarea>, а не в <input type="text">. Смотрим MSDN и видим замечательный note от Microsoft:

Note. This property is applicable only when the TextMode property is set to TextBoxMode.SingleLine or TextBoxMode.Password.

Смотрим <input type="text"> и <textarea> в MSDN – так и есть, атрибут maxLength определен для первого и не определен для второго элемента. Почему так сделано – загадка, но нам нужно как-то с этим бороться. Быстрый поиск дает 2 варианта решения:

  1. Javascript, который не дает ввести больше элементов, чем нужно (вариантов много, например, вот и вот)
  2. Regex-валидатор, который будет проверять размер введенного текста
  3. Custom-валидатор с client-скриптом

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

Выражение для проверки длины текста выглядит очень просто: "^[\s\S]{0,XX}$", где XX – максимально допустимая длина нашего текста. Для более удобного использования можно реализовать свой валидатор на базе стандартного, например, так:

public class MaxLengthValidator : RegularExpressionValidator
{
    private int maxLength = int.MaxValue;

    public MaxLengthValidator()
    {
        int configMaxLength;
        if (int.TryParse(Config.TextBoxMultilineMaxLength, out configMaxLength))
        {
            MaxLength = configMaxLength;
        }
    }

    public int MaxLength
    {
        get { return maxLength; }
        set { maxLength = value; }
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        ValidationExpression = @"^[\s\S]{0," + MaxLength + "}$";
        if (ErrorMessage.Contains("{0}"))
        {
            ErrorMessage = string.Format(ErrorMessage, MaxLength);
        }
    }
}

Как нетрудно догадаться, Config.TextBoxMultilineMaxLength возвращает длину полей ввода из конфигурационного файла. Это удобно, если мы хотим контролировать максимальную длину текста для полей типа ntext и хотим сделать эту настройку system wide. Если вам это не нужно, то можно просто убрать этот код из конструктора и определять MaxLength явно. Также, если мы хотим выдавать сообщение об ошибке, которое будет содержать установленный нами лимит, то это тоже не сложно сделать в событии OnLoad, как сделано в примере. Пользоваться этим валидатором так же просто, как и другими.

No comments:

Post a Comment