在 WinForms/WPF 應用中,有時需要依據欄位屬性(如中文欄位自動切換中文輸入法、英文欄位切回英數)。本文示範如何列舉系統已安裝輸入法、依名稱比對切換、處理找不到時的降級邏輯,並說明 WPF 的 InputMethod 替代方案。
using System;
using System.Linq;
using System.Windows.Forms;
using System.Globalization;
public static class InputMethodHelper
{
///
/// 依 LayoutName 關鍵字尋找並切換輸入法
///
/// 輸入法名稱關鍵字,如 "陳橋", "Microsoft Pinyin", "IME"
/// 找不到時是否回預設輸入法
/// 是否成功切換
public static bool SwitchInputMethod(string keyword, bool fallbackToDefault = true)
{
if (string.IsNullOrWhiteSpace(keyword)) return false;
var installed = InputLanguage.InstalledInputLanguages;
InputLanguage target = null;
// 1. 完全比對 LayoutName
target = installed.Cast()
.FirstOrDefault(il => il.LayoutName.Equals(keyword, StringComparison.OrdinalIgnoreCase));
// 2. 關鍵字模糊比對
if (target == null)
target = installed.Cast()
.FirstOrDefault(il => il.LayoutName.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0);
// 3. 文化特性比對 (如 zh-TW, zh-CN)
if (target == null && CultureInfo.GetCultures(CultureTypes.AllCultures)
.Any(c => c.Name.Contains(keyword, StringComparison.OrdinalIgnoreCase)))
{
var ci = CultureInfo.GetCultures(CultureTypes.AllCultures)
.First(c => c.Name.Contains(keyword, StringComparison.OrdinalIgnoreCase));
target = installed.Cast().FirstOrDefault(il => il.Culture.LCID == ci.LCID);
}
if (target != null)
{
InputLanguage.CurrentInputLanguage = target;
return true;
}
if (fallbackToDefault)
{
InputLanguage.CurrentInputLanguage = InputLanguage.DefaultInputLanguage;
}
return false;
}
///
/// 列印所有已安裝輸入法 (除錯用)
///
public static void PrintInstalledInputMethods()
{
foreach (InputLanguage il in InputLanguage.InstalledInputLanguages)
{
Console.WriteLine($"Culture: {il.Culture.DisplayName} ({il.Culture.Name}) | Layout: {il.LayoutName} | Handle: {il.Handle}");
}
}
}
// 使用範例:在 TextBox Enter/Leave 事件切換
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
txtChinese.Enter += (s, e) => InputMethodHelper.SwitchInputMethod("陳橋"); // 或 "Microsoft Pinyin"
txtEnglish.Enter += (s, e) => InputMethodHelper.SwitchInputMethod("US"); // 美式鍵盤
}
}
WPF 採用 InputMethod 附加屬性,不直接操作 InputLanguage。
using System.Windows.Input;
InputMethod.SetPreferredImeState(txtChinese, ImeState.On);
InputMethod.SetPreferredImeConversionMode(txtChinese, ImeConversionModeValues.Native);
WPF 無法直接指定特定 IME (如陳橋),僅能開啟/關閉 IME 模式。若需強制指定,需 P/Invoke imm32.dll (ImmAssociateContextEx) 或呼叫 WinForms 邏輯 (需參考 System.Windows.Forms)。
using System;
using System.Runtime.InteropServices;
public static class Imm32
{
[DllImport("imm32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr ImmGetContext(IntPtr hWnd);
[DllImport("imm32.dll", CharSet = CharSet.Auto)]
public static extern bool ImmSetConversionStatus(IntPtr hIMC, uint fdwConversion, uint fdwSentence);
[DllImport("imm32.dll", CharSet = CharSet.Auto)]
public static extern bool ImmAssociateContextEx(IntPtr hWnd, IntPtr hIMC, uint dwFlags);
[DllImport("imm32.dll", CharSet = CharSet.Auto)]
public static extern bool ImmGetDescription(IntPtr hkl, StringBuilder lpszDescription, int uBufLen);
public const uint IME_CMODE_NATIVE = 0x0001;
public const uint IME_CMODE_FULLSHAPE = 0x0008;
}
// 使用:取得目標 IME 的 HKL (Keyboard Layout Handle) 並關聯
// 注意:需先列舉系統 HKL (GetKeyboardLayoutList) 比對描述
P/Invoke 複雜且易受 OS 版本影響,非必要不建議。優先使用 InputLanguage.CurrentInputLanguage 切換。
| 現象 | 原因 | 解決 |
|---|---|---|
| 切換無效果 | 目標輸入法未安裝或 LayoutName 不同 | 呼叫 PrintInstalledInputMethods() 確認確切名稱 |
| 切換後立即跳回 | 控制項焦點移動觸發系統自動還原 | 在 Enter 事件切換,避免 Leave 干擾 |
| WPF 無法指定特定 IME | 架構限制 | 使用 WinForms 互通或 P/Invoke |
| 管理員權限下失效 | UIPI (User Interface Privilege Isolation) | 避免跨權限視窗操作,或簽署應用程式 |
public static class InputMethodManager
{
private static readonly Dictionary _cache = new();
static InputMethodManager()
{
foreach (InputLanguage il in InputLanguage.InstalledInputLanguages)
{
_cache[il.LayoutName.ToLowerInvariant()] = il;
_cache[il.Culture.Name.ToLowerInvariant()] = il;
}
}
public static bool TrySet(string identifier) // identifier: "zh-TW", "陳橋", "Microsoft Pinyin", "US"
{
if (_cache.TryGetValue(identifier.ToLowerInvariant(), out var lang))
{
InputLanguage.CurrentInputLanguage = lang;
return true;
}
return false;
}
public static IReadOnlyList GetAvailableIdentifiers() => _cache.Keys.ToList();
}
WinForms 透過 InputLanguage.CurrentInputLanguage 可完整控制輸入法切換;WPF 僅支援開關 IME 狀態。建立快取輔助類別、在控制項 Enter 事件切換、提供降級預設邏輯,即能滿足多數「中文欄位自動切中文輸入法」的實務需求。