SHIFT快捷方式显示为文化“nl-BE”的MAJ,它应该是SHIFT(C#.NET)

时间:2014-04-11 09:20:47

标签: c# localization infragistics culture user32

经过与Infragistics的长时间讨论后,看来带有SHIFT的ShortCuts在我的文化“nl-BE”中显示为MAJ。首先,文化“nl-BE”和AZERTY有点奇怪。如果想了解更多,请阅读http://en.wikipedia.org/wiki/AZERTY。重要的是:

  

其他键是相同的,即使传统上的名称   特殊键用英文印在上面。这是因为比利时   主要是双语(法语 - 荷兰语)和官方三种语言(a   第三语言,德语,在东方语言中使用。)

所以MAJ打印为SHIFT。例如,在Office中,带有SHIFT的快捷方式显示为SHIFT。在Infragistics控件中,它们显示为MAJ。这让我们的客户感到沮丧。

因此,在与Infragistics讨论后,他们声称这是一个Windows Api调用,它返回MAJ而不是SHIFT。我从他们那里得到了一个展示行为的示例项目。所以现在我的问题是为什么Windows Api调用不会返回SHIFT,如果它是正常的,那么Office如何将其显示正确呢?

获取密钥文本的代码是:

NativeWindowMethods.GetKeyNameText((int)scanCode, sb, 256);

class NativeWindowMethods
{
    #region MapVirtualKey
    [DllImport("user32.dll")]
    internal static extern int MapVirtualKey(uint uCode, uint uMapType);
    #endregion //MapVirtualKey

    #region GetKeyNameText
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    internal static extern int GetKeyNameText(
        int lParam, 
        [MarshalAs(UnmanagedType.LPWStr), Out]System.Text.StringBuilder str, 
        int size);
    #endregion //GetKeyNameText
}

如果是Shiftkey,则扫描码为2752512(2a),并返回MAJ。

那么,我的问题是什么?

  • MAJ是否因文化“nl-BE”而被归还是否正常?或者它是user32.dll中的错误?
  • 如果Office能够做到正确,那么Infragistics是否也能做到正确?
  • Infragistics是否使用正确的user32.dll api调用?

为了完整性,我将粘贴Utilities类的完整代码。从表单接下来的调用完成:

systemLocalizedString = Utilities.GetLocalizedShortcutString(shortcut);

使用快捷键= ShiftF12。在调用之后,systemLocalizedString等于“MAJ + F12”。

更新:在Hans Passant的帮助下,我下载了Microsoft Keyboard Layout Creator并导出了我当前的键盘布局。在.klc文件中没有找到MAJ,只有Shift(例如2a Shift)。那么为什么user32.dll会返回MAJ?甚至更奇怪的是,当我制作.klc文件的副本并将其安装为新键盘时,突然user32.dll确实为新安装的键盘返回Shift(虽然它是一个精确的副本)。

Utilities.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
    class Utilities
    {

        #region GetLocalizedShortcutString

        /// <summary>
        /// Returns the localized string for the specified <b>Shortcut</b>
        /// </summary>
        /// <param name="shortcut">Shortcut to localize</param>
        /// <param name="separator">Character used to separate multiple keys in the shortcut</param>
        /// <returns>A string containing the localized description of the shortcut based on the currently mapped keyboard layout</returns>
        public static string GetLocalizedShortcutString(Shortcut shortcut, char separator = '+')
        {
            if (shortcut == Shortcut.None)
                return string.Empty;

            return GetLocalizedKeyString((Keys)shortcut, separator);
        }
        #endregion //GetLocalizedShortcutString

        #region GetLocalizedKeyString

        /// <summary>
        /// Returns the localized string for the specified <b>Keys</b>
        /// </summary>
        /// <param name="keys">Keys to localize</param>
        /// <param name="separator">Character used to separate multiple keys</param>
        /// <returns>A string containing the localized description of the keys based on the currently mapped keyboard layout</returns>
        public static string GetLocalizedKeyString(Keys keys, char separator)
        {
            bool alt = ((long)keys & (long)Keys.Alt) != 0;
            bool ctrl = ((long)keys & (long)Keys.Control) != 0;
            bool shift = ((long)keys & (long)Keys.Shift) != 0;

            // get the key involved
            long value = (long)keys & 0xffff;

            Keys key = (Keys)Enum.ToObject(typeof(Keys), value);
            System.Text.StringBuilder sb = new System.Text.StringBuilder();

            if (alt && key != Keys.Menu)
            {
                sb.Append(GetLocalizedKeyStringHelper(Keys.Menu));
                sb.Append(separator);
            }

            if (ctrl && key != Keys.ControlKey)
            {
                sb.Append(GetLocalizedKeyStringHelper(Keys.ControlKey));
                sb.Append(separator);
            }

            if (shift && key != Keys.ShiftKey)
            {
                sb.Append(GetLocalizedKeyStringHelper(Keys.ShiftKey));
                sb.Append(separator);
            }

            sb.Append(GetLocalizedKeyStringHelper(key));
            return sb.ToString();
        }
        #endregion //GetLocalizedKeyString

        #region GetLocalizedKeyStringHelper
        private static string GetLocalizedKeyStringHelper(Keys key)
        {
            string localizedKey = GetLocalizedKeyStringUnsafe(key);

            if (localizedKey == null || localizedKey.Length == 0)
                return key.ToString();

            return localizedKey;
        }
        #endregion //GetLocalizedKeyStringHelper

        #region GetLocalizedKeyStringUnsafe
        private static string GetLocalizedKeyStringUnsafe(Keys key)
        {
            // strip any modifier keys
            long keyCode = ((int)key) & 0xffff;

            System.Text.StringBuilder sb = new System.Text.StringBuilder(256);

            long scanCode = NativeWindowMethods.MapVirtualKey((uint)keyCode, (uint)0);

            // shift the scancode to the high word
            scanCode = (scanCode << 16);

            if (keyCode == 45 ||
                keyCode == 46 ||
                keyCode == 144 ||
                (33 <= keyCode && keyCode <= 40))
            {
                // add the extended key flag
                scanCode |= 0x1000000;
            }

            NativeWindowMethods.GetKeyNameText((int)scanCode, sb, 256);
            return sb.ToString();
        }
        #endregion //GetLocalizedKeyStringUnsafe
    }

    class NativeWindowMethods
    {
        #region MapVirtualKey
        [DllImport("user32.dll")]
        internal static extern int MapVirtualKey(uint uCode, uint uMapType);
        #endregion //MapVirtualKey

        #region GetKeyNameText
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        internal static extern int GetKeyNameText(int lParam, [MarshalAs(UnmanagedType.LPWStr), Out]System.Text.StringBuilder str, int size);
        #endregion //GetKeyNameText
    }
}

3 个答案:

答案 0 :(得分:5)

  

但即使我正在使用AZERTY,这就是学校用来学习如何输入的内容......

是的,那就是问题所在。获得AZERTY布局的唯一方法是在控制面板+语言+添加语言中选择标题为“français(Belgique)”的键盘布局。与“Nederlands(België)”布局相反,它有QWERTY安排。 GetKeyNameText()winapi函数返回在键盘布局文件中编码的字符串。对于名为français的键盘布局,法语当然是法语,因此MAJ是预期的结果。 Windows没有可用的带有AZERTY排列的荷兰语的键盘布局。

一切都没有丢失,Windows用户经常要求自定义键盘布局。因此,Microsoft提供了一个工具来创建您自己的工具Microsoft Keyboard Layout Creator。它主要用于重新安排钥匙,需要一点额外的肘部油脂才能使它达到你想要的效果。该工具不允许您直接编辑密钥描述并默认使用英文名称。您将要从文件+加载现有键盘开始。然后File + Save Layout将布局保存到.klc文件。在文本编辑器中打开它,记事本没问题,找到名为KEYNAME和KEYNAME_EXT的部分,并按照您希望的方式编辑键名。

重新启动该实用程序(不要跳过)并重新加载.klc文件。并使用Project + Build DLL构建安装程序包。

答案 1 :(得分:4)

假设您使用的是Windows表单,那么您是否尝试使用Windows.Forms.KeysConverter类进行转换?查看正常winforms MenuItem控件的源代码,通过调用KeysConverter.ConvertToString(Object value)方法获取快捷键的字符串。 KeysConverter转而从System.Windows.Forms.resources获取字符串(Shift的“toStringShift”资源键)。这些资源字符串都是本地化的,具体取决于计算机上安装的.Net语言包和用于运行应用程序的文化。所以我可以看到Windows Forms实际上并没有使用user32.dll。

尝试使用以下代码测试并尝试nl-BE和fr-BE(您当然需要安装.Net语言包):

static void Main()
{
    var culture = new CultureInfo("nl-BE");
    Thread.CurrentThread.CurrentCulture = culture;
    Thread.CurrentThread.CurrentUICulture = culture;
    var keys = Keys.Shift | Keys.N;
    var keysstring = new KeysConverter().ConvertToString(keys);
}

如果这确实有效,那么我会说Infragistics正在使用错误的API。我希望他们使用普通的.Net KeysConverter,而不是调用user32.dll。

更新:我无法在计算机上安装语言包但我从安装程序中解压缩文件并确认每种语言的System.Windows.Forms.resources.dll程序集都可以包含您正在寻找的正确的密钥名称。

System.Windows.Forms.fr.resx:

<data name="toStringShift" xml:space="preserve">
  <value>Maj</value>
</data>

System.Windows.Forms.nl.resx:

<data name="toStringShift" xml:space="preserve">
  <value>Shift</value>
</data>

答案 2 :(得分:2)

如果您正在寻找问题的解决方案而不是回答问题谁应该受到指责? Microsoft内核开发人员,Microsoft Office开发人员,Infragistics开发人员......您正在使用WPF,然后使用此

KeyGesture gesture;
MenuItem menuItem;
...
menuItem.InputGestureText = gesture.GetDisplayStringForCulture(System.Threading.Thread.CurrentThread.CurrentUICulture);

让我以我需要的方式显示快捷方式。我没有用深奥或Van-Damme文化测试它,但这段代码应该是语言中立的。

使用Visual Studio debugger如何分析代码的工作原理,KeyGesture.GetDisplayStringForCulture的源代码也可在线浏览→http://referencesource.microsoft.com/#PresentationCore/src/Core/CSharp/System/Windows/Input/Command/KeyGesture.cs#fbe5780461e3961d

编辑 BTW:WPF代码似乎以ModifierKeyConverters结尾

internal static string MatchModifiers(ModifierKeys modifierKeys) { string modifiers = String.Empty; switch (modifierKeys) { case ModifierKeys.Control: modifiers="Ctrl";break; case ModifierKeys.Shift : modifiers="Shift";break; case ModifierKeys.Alt : modifiers="Alt";break; case ModifierKeys.Windows: modifiers="Windows";break; } return modifiers; }