SHIFT快捷键显示为区域性的MAJ“;nl BE”;当它应该是SHIFT(C#.NET)时
本文关键字:SHIFT NET BE 显示 快捷键 区域性 nl MAJ | 更新日期: 2023-09-27 17:57:49
经过与Infrastics的长时间讨论,在我的区域性"nl BE"中,带有SHIFT的ShortCuts显示为MAJ。首先,文化"nl BE"和AZERTY有点奇怪。阅读http://en.wikipedia.org/wiki/AZERTY如果想了解更多。重要的报价是:
其他键是相同的,尽管传统上特殊的钥匙用英文印在上面。这是因为比利时主要是双语(法语-荷兰语)和官方三语(a第三种语言,德语,在东部各州使用)。
所以MAJ被打印为SHIFT。例如,在Office中,带有SHIFT的快捷方式显示为SHIFT。然而,在Infrastics控件中,它们显示为MAJE。这让我们的客户感到沮丧。
因此,在与Infrastics讨论后,他们声称这是一个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做对了,难道不应该由Infrastics来做对吗
- Infrasgistics是否使用正确的user32.dll api调用
为了完整起见,我将粘贴Utilities类的完整代码。从表单完成下一次调用:
systemLocalizedString=Utilities。GetLocalizedShortcutString(快捷方式);
使用快捷键=ShiftF12。调用后,systemLocalizedString等于"MAJ+F12"。
更新:在Hans Passant的帮助下,我下载了Microsoft Keyboard Layout Creator并导出了我当前的键盘布局。在.klc文件中找不到MAJ,只有Shift(例如2a Shift)。那么,为什么user32.dll返回MAJ呢?更奇怪的是,当我复制.klc文件并将其作为新键盘安装时,突然user32.dll会为新安装的键盘返回Shift(尽管它是一个精确的副本)。
实用程序.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
}
}
但即使我使用AZERTY,这也是学校用来学习如何打字的,所以…
所以,是的,这就是问题所在。获得AZERTY布局的唯一方法是在控制面板+语言+添加语言中选择标题为"法语(比利时)"的键盘布局。与"Nederlands(België)"布局不同,它采用QWERTY布局。GetKeyNameText()winapi函数返回键盘布局文件中编码的字符串。当然,对于名为français的键盘布局来说,它们是法语的,所以MAJ是预期的结果。Windows没有使用AZERTY排列的荷兰语键盘布局。
一切都没有失去,Windows用户经常要求自定义键盘布局。因此,微软提供了一个工具来创建你自己的,微软键盘布局创建器。它的主要设计目的是重新排列按键,需要一点额外的肘部油脂才能让它做你想做的事。该工具不允许您直接编辑密钥描述,并且默认为英文名称。您需要从"文件+加载现有键盘"开始。然后File+Save Layout将布局保存到.klc文件中。在文本编辑器中打开它,记事本就可以了,找到名为KEYNAME和KEYNAME_EXT的部分,并按照您想要的方式编辑键名。
重新启动实用程序(不要跳过)并重新加载.klc文件。并使用Project+build DLL构建安装程序包。
假设您使用的是Windows窗体,您是否尝试过使用Windows.Forms.KeysConverter
类进行转换?查看普通winforms MenuItem
控件的源代码,快捷键的字符串是通过调用KeysConverter.ConvertToString(Object value)
方法获得的。KeysConverter依次从System.Windows.Forms.resources获取字符串(Shift的"toStringShift"资源键)。这些资源字符串都是本地化的,具体取决于计算机上安装的.Net语言包以及用于运行应用程序的区域性。因此,从我所看到的Windows窗体实际上并没有使用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);
}
如果这确实有效,那么我会说Infrastics使用了错误的API。我希望他们使用普通的.Net密钥转换器,而不是调用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>
如果你正在寻找问题的解决方案,而不是回答这个问题,谁应该受到指责?Microsoft内核开发人员、Microsoft Office开发人员、Infrastics开发人员。。如果你使用的是WPF,那么这个
KeyGesture gesture;
MenuItem menuItem;
...
menuItem.InputGestureText = gesture.GetDisplayStringForCulture(System.Threading.Thread.CurrentThread.CurrentUICulture);
适用于我以需要的方式显示快捷方式。我没有用深奥的或Van Damme文化来测试它,但这个代码应该是语言中立的。
使用Visual Studio调试器可以分析代码的确切工作方式,KeyGesture.GetDisplayStringForCulture
的源代码也可以在线浏览→http://referencesource.microsoft.com/#PresentationCore/src/Core/CSharp/System/Windows/Input/Command/KeyGesture.cs#fbe5780461e3961d
EDITBTW: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;
}