最近刚刚换了工作,到了新公司的接到第一个项目,是做一个分店零售POS系统。客户对界面要求比较高,所以采用了WPF做为客户端开发技术。在POS系统中,快捷键是比较重要的功能,有很多快捷键在功能是实现上相似,WPF需要在每个页面上配置InputBindings。为了维护、开发便利,对快捷键做统一管理,变得很有必要。现在我来介绍一下实现。
要做到统一管理,我的想法就是让快捷键变成可配置的,其次,能根据配置自动绑定快捷键及处理句柄。废话不多说直接上代码。
1、KeyboardShortcuts 快捷键统一管理的入口
public class KeyboardShortcuts { private const string COFINGFILE = "KeyboardShortcutsRule.Config"; public KeyboardShortcuts() { Rules = new List<KeyboardShortcutsRule>(); LoadConfig(); } public List<KeyboardShortcutsRule> Rules { get; set; } public void ActiveKeysBindings(Window window) { foreach (var item in Rules) { if (!item.IgnoreWindow.Contains(window.GetType())) { if (item.Effectivity.Count == 0 || item.Effectivity.Contains(window.GetType())) { window.InputBindings.Add(new InputBinding(item.Command.GetCommand(), new KeyGestureConverter().ConvertFromString(item.Keys) as KeyGesture) { CommandParameter = window }); } } } } private static KeyboardShortcuts current = null; public static KeyboardShortcuts Current { get { if (current == null) { current = new KeyboardShortcuts(); } return current; } } public void LoadConfig() { try { XDocument doc = XDocument.Load(COFINGFILE); this.Rules = doc.Element("Settings").Elements("KeyboardShortcuts") .Select(o => new KeyboardShortcutsRule() { Name = o.Attribute("Name").Value, IsShowInHelp = o.Attribute("IsShowInHelp") != null ? Convert.ToBoolean(o.Attribute("IsShowInHelp").Value) : true, Keys = o.Attribute("Keys").Value, IgnoreWindow = new List<Type>().InitListType(o.Elements("Ignore")), Effectivity = new List<Type>().InitListType(o.Elements("Effect")), Command = o.Attribute("Command").Value.InitCommand() } ).ToList(); } catch { } } public void SaveConfig() { try { XDocument doc = new XDocument(); var element = new XElement("Settings"); foreach (var item in Rules) { var cmdType = item.Command.GetType(); XElement node = new XElement( "KeyboardShortcuts", new XAttribute("Name", item.Name), new XAttribute("Keys", item.Keys), new XAttribute("IsShowInHelp", item.IsShowInHelp), new XAttribute("Command", string.Format("{0}|{1}", cmdType.Assembly.FullName, cmdType.ToString()))); foreach (var childItem in item.IgnoreWindow) { XElement ignoreList = new XElement("Ignore", childItem); node.Add(ignoreList); } foreach (var childItem in item.Effectivity) { XElement effectList = new XElement("Effect", childItem); node.Add(effectList); } element.Add(node); } doc.Add(element); doc.Save(COFINGFILE); } catch { } } }
2、快捷键配置规则实体
public class KeyboardShortcutsRule { public KeyboardShortcutsRule() { IsShowInHelp = true; } public string Name { get; set; } public string Keys { get; set; } public bool IsShowInHelp { get; set; } public List<Type> IgnoreWindow { get; set; } public List<Type> Effectivity { get; set; } public IKeyboardShortcutsCommand Command { get; set; } }
3、对XML TO LINQ的扩展
public static class LinqExtensions { public static List<Type> InitListType(this List<Type> _this, IEnumerable<XElement> ignoreNodes) { if (_this == null) _this = new List<Type>(); foreach (var item in ignoreNodes) { _this.Add(Assembly.GetExecutingAssembly().GetType(item.Value)); } return _this; } public static IKeyboardShortcutsCommand InitCommand(this string _this) { var results = _this.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); if (results.Count() == 2) { return Assembly.Load(results[0]).CreateInstance(results[1]) as IKeyboardShortcutsCommand; } else { return Assembly.GetExecutingAssembly().CreateInstance(results[0]) as IKeyboardShortcutsCommand; } } }
4、快捷键处理命令接口【实现这个接口的对象,在绑定快捷键后将被自动激发,PosCommand实现了WPF的ICommand接口】
public interface IKeyboardShortcutsCommand { PosCommand GetCommand(); string Describe { get; set; } }
5、PosCommand实现了WPF的ICommand接口
public class PosCommand : ICommand { public PosCommand() { } public PosCommand(Action func) { Processor = func; } public PosCommand(Action func, string describe) { Processor = func; Describe = describe; } public PosCommand(Action<object> func, string describe) { ProcessorForParameter = func; Describe = describe; } public string Describe { get; set; } public Action Processor { get; set; } public Action<object> ProcessorForParameter { get; set; } public bool CanExecute(object parameter) { if (Processor != null) return true; if (ProcessorForParameter != null) { return true; } return false; } public void Execute(object parameter) { if (Processor != null) Processor(); if (ProcessorForParameter != null) ProcessorForParameter(parameter); } public event EventHandler CanExecuteChanged; }
6、实现快捷键绑定命令
public class HelpCommand : IKeyboardShortcutsCommand { public HelpCommand() { Describe = "帮助"; } public PosCommand GetCommand() { return new PosCommand((o) => { Help page = new Help(); page.Show(); }, Describe); } public string Describe { get; set; } }
7、配置
<?xml version="1.0" encoding="utf-8"?> <Settings> <KeyboardShortcuts Name="Help" Keys="F1" IsShowInHelp="true" Command="Pharos.POS.Retailing.HelpCommand"> <Ignore>Pharos.POS.Retailing.Login</Ignore> </KeyboardShortcuts> <KeyboardShortcuts Name="ExitSystem" Keys="Alt+F4" Command="Pharos.POS.Retailing.ExitSystemCommand"> <Effect>Pharos.POS.Retailing.MainWindow</Effect> <Effect>Pharos.POS.Retailing.Login</Effect> </KeyboardShortcuts> <KeyboardShortcuts Name="LockScreen" Keys="Ctrl+D" Command="Pharos.POS.Retailing.LockScreenCommand"> <Effect>Pharos.POS.Retailing.MainWindow</Effect> </KeyboardShortcuts> <KeyboardShortcuts Name="Cannel" Keys="Escape" Command="Pharos.POS.Retailing.CannelCommand"></KeyboardShortcuts> </Settings>
8、引用,并启用快捷键设置
public static class WindowExtensions { public static void InitPublicSettings(this Window _this) { //激活快捷键 KeyboardShortcuts.Current.ActiveKeysBindings(_this); } }
public partial class Login : Window { public Login() { InitializeComponent(); this.InitPublicSettings(); } }
总结,这么做的好处就是让快捷键自定义变得很容易,只需要实现IKeyboardShortcutsCommand,配置规则文档。但是也带来了安全性问题,任何人只要稍微了解这里的机制,就可以利用快捷键的反射机制,注入程序木马等等问题,就算不懂电脑编程的,删除该配置文件也会造成快捷键失效等等问题。