Почему нажатие клавиши Shift в DGV вызывает исключение?

У меня есть этот код в пользовательском классе, полученном из DataGridView:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    if (char.IsNumber(Convert.ToChar(keyData)) || 
        char.IsControl(Convert.ToChar(keyData)) || 
        (keyData >= Keys.NumPad0 && keyData <= Keys.NumPad9) ||
        (keyData == Keys.Up) ||
        (keyData == Keys.Down) ||
        (keyData == Keys.Left) ||
        (keyData == Keys.Right) ||
        (keyData == Keys.Home) ||
        (keyData == Keys.PageDown) ||
        (keyData == Keys.PageUp) ||
        (keyData == Keys.Space) ||
        (keyData == Keys.Back) ||
        (keyData == Keys.Decimal))
    {
        return false;
    }
    return true;
}

Я могу вводить данные (числа и .) и переходить от ячейки к ячейке, но если я нажимаю клавишу «Shift», я получаю «System.OverflowException не было обработано кодом пользователя. Сообщение = Значение было слишком большим или слишком мал для символа. Source=mscorlib StackTrace: at System.Convert.ToChar(значение Int32)..."

Я предполагаю, что это строка, которая вызывает проблему:

char.IsControl(Convert.ToChar(keyData))

... но почему Shift проблематичен и что мне делать, чтобы он игнорировал клавишу Shift (у пользователя нет причин нажимать клавишу Shift в DGV).

ОБНОВИТЬ

Должен признаться, я не совсем понимаю ответ Кетцалькоатля, но я попытался применить его следующим образом:

Keys specials = keyData & Keys.Modifiers; 
Keys keycode = keyData & ~Keys.Modifiers;

if (char.IsNumber(Convert.ToChar(keyData)) || 
    //char.IsControl(Convert.ToChar(keyData)) || 
    //(keyData == specials) || <-- didn't work
    //(keyData == keycode) ||  <-- didn't work
    (keyData != specials) ||
    (keyData != keycode) ||
    . . .

... и все равно получаю ту же ошибку. Как я могу применить предоставленную им информацию для решения проблемы?

ОБНОВЛЕНИЕ 2

Я все еще борюсь с этим.

Я пробовал это:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    Keys keycode = keyData & ~Keys.Modifiers;

    if (keycode >= Keys.D0 && keycode <= Keys.D9 ||          
        keycode >= Keys.NumPad0 && keycode <= Keys.NumPad9 ||
        (keycode == Keys.Up) ||
        (keycode == Keys.Down) ||
        (keycode == Keys.Left) ||
        (keycode == Keys.Right) ||
    (keyData == Keys.Tab) ||
        (keycode == Keys.Tab) ||
        (keycode == Keys.Home) ||
        (keycode == Keys.PageDown) ||
        (keycode == Keys.PageUp) ||
        (keycode == Keys.Space) ||
        (keycode == Keys.Back) ||
        (keycode == Keys.Decimal))
    {
        return false;
    }
    return true;
}

... и этот подход:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        Keys keycode = keyData & ~Keys.Modifiers;

        bool isDigit = keycode >= Keys.D0 && keycode <= Keys.D9 ||
                       keycode >= Keys.NumPad0 && keycode <= Keys.NumPad9;
        bool isControl = (keyData & Keys.Modifiers) != Keys.None;

        if (isDigit ||
            isControl ||
            (keyData == Keys.Up) ||
            (keyData == Keys.Down) ||
            (keyData == Keys.Left) ||
            (keyData == Keys.Right) ||
        (keyData == Keys.Tab) ||
            (keyData == Keys.Home) ||
            (keyData == Keys.PageDown) ||
            (keyData == Keys.PageUp) ||
            (keyData == Keys.Space) ||
            (keyData == Keys.Back) ||
            (keyData == Keys.Decimal))
        {
            return false;
        }
        return true;
}

Оба они решают, что клавиша Shift вызывает переполнение, но оба они предотвращают ввод десятичной дроби. Я этого не понимаю, поскольку Decimal (предположительно) явно разрешен. Все остальное, что явно проверено, разрешено (Home, PageUp, PageDown и т. д.) - это просто "." персонаж, которому запрещен вход.

ОБНОВЛЕНИЕ 3

Использование второго подхода выше и замена «Keys.Decimal» на «Keys.OemPeriod» работает:

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    Keys keycode = keyData & ~Keys.Modifiers;

    bool isDigit = keycode >= Keys.D0 && keycode <= Keys.D9 ||
                   keycode >= Keys.NumPad0 && keycode <= Keys.NumPad9;
    bool isControl = (keyData & Keys.Modifiers) != Keys.None;

    if (isDigit ||
        isControl ||
        (keyData == Keys.Up) ||
        (keyData == Keys.Down) ||
        (keyData == Keys.Left) ||
        (keyData == Keys.Right) ||
        (keyData == Keys.Home) ||
        (keyData == Keys.Tab) ||
        (keyData == Keys.PageDown) ||
        (keyData == Keys.PageUp) ||
        (keyData == Keys.Space) ||
        (keyData == Keys.Back) ||
        (keyData == Keys.OemPeriod))
    {
        return false;
    }
    return true;
}

person B. Clay Shannon    schedule 28.09.2012    source источник


Ответы (3)


Не конвертируйте Keys в char. Это не работает. Проверьте, является ли это цифрой с

Keys code = keyData & ~Keys.Modifiers;

bool isDigit = code >= Keys.D0 && code <= Keys.D9 ||
               code >= Keys.NumPad0 && code <= Keys.NumPad9

Проверьте, является ли это клавишей управления с

bool isControl = (keyData & Keys.Modifiers) != Keys.None;
person Olivier Jacot-Descombes    schedule 28.09.2012
comment
Я помечаю это как ответ, потому что все, что мне нужно было сделать с моим исходным кодом, это включить это и заменить Keys.Decimal на Keys.OemPeriod (см. Обновление 3 к моему исходному сообщению). - person B. Clay Shannon; 01.10.2012

Это потому, что вы неправильно переводите Keys keyData в Char. Convert.ToChar не справляется с этим.

Перечисление Keys содержит маску, которую следует использовать для фильтрации всех модификаторов, таких как shift, alt, control:

Keys specials = keyData & Keys.Modifiers;
Keys keycode = keyData & ~Keys.Modifiers;

Теперь keycode равно ровно одному из обычных значений A/B/C/..., определенных в Keys, а specials представляет собой набор битов, состоящий из модификаторов Control/Alt/Shift.

Точная причина ошибки заключается в том, что хотя некоторые из «младших значений ключа» (a,b,c,..z,0-9 и т. д.) фактически равны/похожи на коды ASCII, тогда «модификаторы» предоставляется как очень большое целое число, которое побитовое ИЛИ к базовому ключевому коду:

Keys justA = Keys.A;
Keys theAandShift = Keys.A | Keys.Shift;

и т. д. Это приводит к тому, что «A with Shift» имеет числовое значение, совершенно абсурдное в смысле ASCII или даже Unicode ... и поэтому вы должны сначала отрезать коды модификаторов!

редактировать:

Keys specials = keyData & Keys.Modifiers;
Keys keycode = keyData & ~Keys.Modifiers;

bool withShift = specials.HasFlag( Keys.Shift );
bool withControl = specials.HasFlag( Keys.Control );
bool withAlt = specials.HasFlag( Keys.Alt );

if(keycode == Keys.F1 && !withShift && !withControl && !withAlt)
    ; // it was plain F1 with no specials

else if(keycode == Keys.VolumeUp)
    ; // it was 'volume-up' key with any or none of the modifiers

else if(keycode >= Keys.A && keycode <= Keys.Z && withControl)
    ; // it was CTRL+Letter

else if(keycode >= Keys.D0 && keycode <= Keys.D9 && withShift)
    ; // it was SHIFT+Digit

и так далее

person quetzalcoatl    schedule 28.09.2012
comment
Хорошо, я (достаточно) уверен, что вы правы, но я не знаю, как применить то, что вы говорите, для решения моей проблемы. Я обновил свой пост, чтобы показать, что я пробовал, но ничего не изменилось. - person B. Clay Shannon; 29.09.2012
comment
Пожалуйста, проверьте редактирование, я включил дерево if, показывающее, как проверять ключи и модификаторы. Обратите внимание, что я оставил переменную specials, несмотря на то, что она не важна: нужна только отфильтрованная keycode, а 'withshift' (и подобные) можно прочитать непосредственно из данных ключей. Я имею в виду, что specials.HasFlag(Keys.Shift) полностью равно keyData.HasFlag(Keys.Shift). Я включил переменную specials только для того, чтобы показать, как организованы старшая и младшая части keyData. Старшие биты — это модификаторы (Shift, Control, Alt), а младшие биты — это коды клавиш. - person quetzalcoatl; 29.09.2012
comment
Кстати, именно поэтому в перечислении Keys у вас есть члены Control и RControlKey LControlKey: Control — это модификатор, а L/RControlKey — код клавиши. Надеюсь это поможет:) - person quetzalcoatl; 29.09.2012
comment
Действительно ли это самый простой способ выбросить клавишу Shift? - person B. Clay Shannon; 29.09.2012
comment
А, а также обратите внимание, что я намеренно не использую метод Convert.ToChar(). Convert.ToChar используется для ASCII-подобных преобразований, в то время как перечисление Keys имеет некоторые значения, которые не соответствуют значениям ASCII, поэтому Convert+Char.IsDigit и подобные могут завершиться ошибкой. Вот почему лучше просто сравнить с Ключами, как это сделал я. - person quetzalcoatl; 29.09.2012
comment
Нет, я показал вам полную сторону проблемы. Самый простой способ - просто отфильтровать модификаторы: просто включите в начало вашего метода строку keyData &= ~Keys.Modifiers; и все готово, все старшие биты обнуляются и исчезают. Но все же не гарантируется, что Convert.ToChar() будет работать для всех клавиш (например, F12 или VolumeUp). Хотя они могут случайно сработать :) Я имею в виду, что для A-Z и D0-D9 это, вероятно, будет нормально. - person quetzalcoatl; 29.09.2012

Keys нельзя преобразовать в char вот так. Если вы посмотрите на Shift в Keys, оно имеет значение 65536, что выходит за пределы для char

person Mike Park    schedule 28.09.2012