WPF之全局快捷鍵

目錄

1.WPF快捷鍵實現方式

2.全局快捷鍵設置界面

3.Windows API調用

4.註冊全局快捷鍵

5.快捷鍵觸發

 

 

WPF快捷鍵實現方式 


     WPF快捷鍵實現主要有自定義快捷鍵命令和全局快捷鍵兩種方式。前端

    自定義快捷鍵命令方式是經過KeyBinding爲命令綁定快捷鍵,按鍵組合可以使用「+」進行鏈接。能夠經過Modifiers+KeyGesture兩種方式定義快捷鍵組合。能夠任選其一進行使用,MSDN中建議使用Gesture方式定義以避免發生混淆。ide

  <Window.InputBindings>
        <KeyBinding Modifiers="Control+Alt" Key="Z" Command="{StaticResource CaptureScreen}" />
        <KeyBinding Gesture="Control+Alt+Q" Command="{StaticResource FullScreen}" />
  </Window.InputBindings>

 

    全局快捷鍵方式是經過調用Windows APIRegisterHotKey函數來實現全局快捷鍵註冊,調用UnregisterHotKey函數實現全局快捷鍵註銷。這種方式WinFormWPF通用。和自定義命令方式不一樣的是這種方式是在系統範圍內定義熱鍵,而前者是在窗口範圍內定義。窗口範圍內定義的快捷鍵觸發條件不只要求窗口可見,而且要求窗口獲取鍵盤焦點。這裏引入的問題是,若是命令的目標不具有獲取鍵盤焦點的能力,則命令將會無效。而且,和系統範圍內定義的快捷鍵相沖突時,優先級要低。函數

    若是是Ribbon界面菜單,推薦使用自定義快捷鍵命令的方式。經過CanExecute方法控制當前命令在目標元素上是否可用,目標元素顯示可用或禁用狀態。若是是窗口無焦點下觸發快捷鍵,則只能選用全局快捷鍵方式了。this

 

全局快捷鍵設置界面 


     如下是熱鍵設置的界面。接下來對全局快捷鍵的實現分步驟說明。spa

 

    這是XAML頁面的代碼,這裏有界面元素的定義。線程

...... 
    <ItemsControl Margin="10" ItemsSource="{Binding HotKeyList,ElementName=win}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Grid Margin="7">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>
                            <CheckBox Grid.Column="0"
                                      Content="{Binding Name}"
                                      IsChecked="{Binding IsUsable}"
                                      Style="{StaticResource ckbStyle1}" />
                            <CheckBox  Grid.Column="1"
                                       Content="Ctrl"
                                       IsChecked="{Binding IsSelectCtrl}"
                                       IsEnabled="{Binding IsUsable}"
                                       Style="{StaticResource ckbStyle2}" />
                            <CheckBox  Grid.Column="2"
                                       Content="Shift"
                                       IsChecked="{Binding IsSelectShift}"
                                       IsEnabled="{Binding IsUsable}"
                                       Style="{StaticResource ckbStyle2}" />
                            <CheckBox  Grid.Column="3"
                                       Content="Alt"
                                       IsChecked="{Binding IsSelectAlt}"
                                       IsEnabled="{Binding IsUsable}"
                                       Style="{StaticResource ckbStyle2}" />
                            <ComboBox  Grid.Column="4"
                                       ItemsSource="{Binding Keys}"
                                       SelectedItem="{Binding SelectKey}"
                                       IsEnabled="{Binding IsUsable}"
                                       Style="{StaticResource cmbStyle1}" />
                        </Grid>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
    ......

 

    首先,新建一個自定義按鍵枚舉。WinForm中能夠使用Keys枚舉轉換,WPFKey枚舉是不正確的,應該使用system.Windows.Froms.Keys枚舉,或者自定義正確的枚舉或int常量。由於這裏定義的枚舉會做爲快捷鍵設置的可選項,能夠只定義須要擊鍵。code

    /// <summary> 
/// 自定義按鍵枚舉 /// </summary> public enum EKey { Space = 32, Left = 37, Up = 38, Right = 39, Down = 40, A = 65, B = 66, C = 67, D = 68, ...... }

 

    新建快捷鍵模型類。在模型中,能夠直接將EKey枚舉值集合綁定到界面的ComboBox上。orm

    /// <summary>
    /// 快捷鍵模型
    /// </summary>
    public class HotKeyModel
    {
        /// <summary>
        /// 設置項名稱
        /// </summary>
        public string Name { get; set; } 

        /// <summary>
        /// 設置項快捷鍵是否可用
        /// </summary>
        public bool IsUsable { get; set; } 

        /// <summary>
        /// 是否勾選Ctrl按鍵
        /// </summary>
        public bool IsSelectCtrl { get; set; } 

        /// <summary>
        /// 是否勾選Shift按鍵
        /// </summary>
        public bool IsSelectShift { get; set; }

         /// <summary>
        /// 是否勾選Alt按鍵
        /// </summary>
        public bool IsSelectAlt { get; set; }

         /// <summary>
        /// 選中的按鍵
        /// </summary>
        public EKey SelectKey { get; set; } 

        /// <summary>
        /// 快捷鍵按鍵集合
        /// </summary>
        public static Array Keys
        {
            get
            {
                return Enum.GetValues(typeof(EKey));
            }
        }
    }

 

Windows API調用 


     WM_HOTKEY爲熱鍵消息,在用戶鍵入被RegisterHotKey函數註冊的熱鍵時發送。該消息將位於隊列的最前端,而且與註冊了這個熱鍵的進程關聯。blog

    RegisterHotKey函數定義一個系統範圍內的熱鍵。 參數hWnd是接收熱鍵產生WM_HOTKEY消息的窗口句柄。若該參數null,傳遞給調用線程的WM_HOTKEY消息必須在消息循環中進行處理。 參數id爲定義熱鍵的標識符。調用線程中的其餘熱鍵,不能使用一樣的標識符。爲了不與其餘動態連接庫定義的熱鍵衝突,一個DLL必須使用GlobalAddAtom函數獲取熱鍵的標識符。 參數fsModifiers是定義爲了產生WM_HOTKEY消息而必須與由nVirKey參數定義的鍵一塊兒按下的鍵,即CtrlShiftAlt的按鍵組合。 參數vk是定義熱鍵的虛擬鍵碼,也就是選中的EKey中的按鍵。 PS:當某鍵被接下時,系統在全部的熱鍵中尋找匹配者。一旦找到一個匹配的熱鍵,系統將把WM_HOTKEY消息傳遞給登記了該熱鍵的線程的消息隊列。該消息被傳送到隊列頭部,所以它將在下一輪消息循環中被移除。該函數不能將熱鍵同其餘線程建立的窗口關聯起來。若爲一熱鍵定義的擊鍵已被其餘熱鍵所定義,則RegisterHotKey函數調用失敗。若hWnd參數標識的窗口已用與id參數定義的相同的標識符登記了一個熱鍵,則參數fsModifiersvk的新值將替代這些參數先前定義的值。隊列

    UnregisterHotKey函數釋放調用線程先前登記的熱鍵。 參數hWnd與被釋放的熱鍵相關的窗口句柄。若熱鍵不與窗口相關,則該參數爲null

    GlobalAddAtom函數是向全局原子表添加一個字符串,並返回這個字符串的惟一標識符(原子ATOM)。Win32系統中,爲了實現信息共享,系統維護了一張全局原子表,用於保存字符串與之對應的標識符的組合。 參數lpString爲一個字符串,這個字符串的長度最大爲255字節。返回值爲一個short類型的原子。若函數調用失敗,則返回值爲0。 PS:若是字符串中已經存在於全局原子表中,則返回現有的字符串的原子,而且原子的引用計數加1。與原子相關的字符串不會從內存中刪除,直到它的引用計數爲零。全局原子不會在應用程序終止時自動刪除。每次調用GlobalAddAtom函數,必須相應的調用GlobalDeleteAtom函數刪除原子。

    GlobalFindAtom函數是在表中搜索全局原子。 參數lpString爲一個字符串。找到返回原子,沒找到返回0

    GlobalDeleteAtom函數是在表中刪除全局原子。 參數nAtom爲全局原子。

    /// <summary>
    /// 熱鍵管理器
    /// </summary>
    public class HotKeyManager
    {
        /// <summary>
        /// 熱鍵消息
        /// </summary>
        public const int WM_HOTKEY = 0x312;

        /// <summary>
        /// 註冊熱鍵
        /// </summary>
        [DllImport("user32.dll", SetLastError = true)]
        public static extern bool RegisterHotKey(IntPtr hWnd, int id, ModifierKeys fsModifuers, int vk);

        /// <summary>
        /// 註銷熱鍵
        /// </summary>
        [DllImport("user32.dll", SetLastError = true)]
        public static extern bool UnregisterHotKey(IntPtr hWnd, int id);

         /// <summary>
        /// 向原子表中添加全局原子
        /// </summary>
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern short GlobalAddAtom(string lpString);
 
        /// <summary>
        /// 在表中搜索全局原子
        /// </summary>
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern short GlobalFindAtom(string lpString); 

        /// <summary>
        /// 在表中刪除全局原子
        /// </summary>
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern short GlobalDeleteAtom(string nAtom);
    }

 

註冊全局快捷鍵  


     首先,重載OnSourceInitialized函數,這個事件發生在WPF窗體的資源初始化完成,而且能夠經過WindowInteropHelper得到該窗體的句柄用來與Win32交互後。調用HwndSource.FromHwnd方法獲取當前窗口句柄,再調用hWndSource.AddHook方法添加接收全部窗口消息的事件處理程序。重載OnContentRendered函數,在控件初始化完成後初始化快捷鍵。

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);
            // 獲取窗體句柄
            m_Hwnd = new WindowInteropHelper(this).Handle;
            HwndSource hWndSource = HwndSource.FromHwnd(m_Hwnd);
            // 添加處理程序
            if (hWndSource != null) hWndSource.AddHook(WndProc);
        }

        /// <summary>
        /// 全部控件初始化完成後調用
        /// </summary>
        /// <param name="e"></param>
        protected override void OnContentRendered(EventArgs e)
        {
            base.OnContentRendered(e);
            // 註冊熱鍵
            InitHotKey();
        }

 

    註冊全局快捷鍵到系統中。

        /// <summary>
        /// 初始化註冊快捷鍵
        /// </summary>
        /// <param name="hotKeyModelList">待註冊熱鍵的項</param>
        /// <returns>true:保存快捷鍵的值;false:彈出設置窗體</returns>
        private bool InitHotKey(ObservableCollection<SysParameterSettingHotKeyModel> hotKeyModelList = null)
        {
            var list = hotKeyModelList ?? SysParameterSettingsManager.Instance.LoadDefaultHotKey();
            // 註冊全局快捷鍵
            string failList = HotKeyHelper.RegisterGlobalHotKey(list, m_Hwnd, out m_HotKeySettings);
            if (string.IsNullOrEmpty(failList))
                return true;

            MessageBoxResult mbResult = MessageBoxWindow.Show("提示", string.Format("沒法註冊下列快捷鍵\n\r{0}是否要改變這些快捷鍵?", failList), MessageBoxButton.YesNo);
            var win = SysParameterSettingsWindow.CreateInstance();
            if (mbResult == MessageBoxResult.Yes)
            {
                win.hotKeySet.IsSelected = true;
                if (!win.IsVisible)
                {
                    win.ShowDialog();
                }
                else
                {
                    win.Activate();
                }
                return false;
            }
            return true;
        }

 

    新建一個熱鍵註冊幫助類HotKeyHelper。這裏有兩個須要注意的地方。第一個是全局原子不會在應用程序終止時自動刪除,每次調用GlobalAddAtom函數,必須相應的調用GlobalDeleteAtom函數刪除原子。第二是若爲一熱鍵定義的擊鍵已被其餘熱鍵所定義,則RegisterHotKey函數調用失敗,因此每次調用RegisterHotKey函數註冊熱鍵時,必須先調用UnregisterHotKey函數註銷舊的熱鍵。 

    /// <summary>
    /// 熱鍵註冊幫助
    /// </summary>
    public class HotKeyHelper
    {
        /// <summary>
        /// 記錄快捷鍵註冊項的惟一標識符
        /// </summary>
        private static Dictionary<EHotKeySetting, int> m_HotKeySettingsDic = new Dictionary<EHotKeySetting, int>();

        /// <summary>
        /// 註冊系統快捷鍵
        /// </summary>
        /// <param name="hotKeyModelList">待註冊快捷鍵項</param>
        /// <param name="hwnd">窗口句柄</param>
        /// <param name="hotKeySettingsDic">快捷鍵註冊項的惟一標識符字典</param>
        /// <returns>返回註冊失敗項的拼接字符串</returns>
        public static string RegisterSystemHotKey(IEnumerable<HotKeyModel> hotKeyModelList, IntPtr hwnd, out Dictionary<EHotKeySetting, int> hotKeySettingsDic)
        {
            string failList = string.Empty;
            foreach (var item in hotKeyModelList)
            {
                if (!RegisterHotKey(item, hwnd))
                {
                    string str = string.Empty;
                    if (item.IsSelectCtrl && !item.IsSelectShift && !item.IsSelectAlt)
                    {
                        str = ModifierKeys.Control.ToString();
                    }
                    else if (!item.IsSelectCtrl && item.IsSelectShift && !item.IsSelectAlt)
                    {
                        str = ModifierKeys.Shift.ToString();
                    }
                    else if (!item.IsSelectCtrl && !item.IsSelectShift && item.IsSelectAlt)
                    {
                        str = ModifierKeys.Alt.ToString();
                    }
                    else if (item.IsSelectCtrl && item.IsSelectShift && !item.IsSelectAlt)
                    {
                        str = string.Format("{0}+{1}", ModifierKeys.Control.ToString(), ModifierKeys.Shift);
                    }
                    else if (item.IsSelectCtrl && !item.IsSelectShift && item.IsSelectAlt)
                    {
                        str = string.Format("{0}+{1}", ModifierKeys.Control.ToString(), ModifierKeys.Alt);
                    }
                    else if (!item.IsSelectCtrl && item.IsSelectShift && item.IsSelectAlt)
                    {
                        str = string.Format("{0}+{1}", ModifierKeys.Shift.ToString(), ModifierKeys.Alt);
                    }
                    else if (item.IsSelectCtrl && item.IsSelectShift && item.IsSelectAlt)
                    {
                        str = string.Format("{0}+{1}+{2}", ModifierKeys.Control.ToString(), ModifierKeys.Shift.ToString(), ModifierKeys.Alt);
                    }
                    str += string.Format("+{0}", item.SelectKey.ToString());
                    str = string.Format("{0} ({1})\n\r", item.Name, str);
                    failList += str;
                }
            }
            hotKeySettingsDic = m_HotKeySettingsDic;
            return failList;
        }
 

        /// <summary>
        /// 註冊熱鍵
        /// </summary>
        /// <param name="hotKeyModel">熱鍵待註冊項</param>
        /// <param name="hWnd">窗口句柄</param>
        /// <returns>成功返回true,失敗返回false</returns>
        private static bool RegisterHotKey(HotKeyModel hotKeyModel, IntPtr hWnd)
        {
            var fsModifierKey = new ModifierKeys();
            var hotKeySetting = (EHotKeySetting)Enum.Parse(typeof(EHotKeySetting), hotKeyModel.Name); 

            if (!m_HotKeySettingsDic.ContainsKey(hotKeySetting))
            {
                // 全局原子不會在應用程序終止時自動刪除。每次調用GlobalAddAtom函數,必須相應的調用GlobalDeleteAtom函數刪除原子。
                if (HotKeyManager.GlobalFindAtom(hotKeySetting.ToString()) != 0)
                {                    HotKeyManager.GlobalDeleteAtom(HotKeyManager.GlobalFindAtom(hotKeySetting.ToString()));
                }
                // 獲取惟一標識符
                m_HotKeySettingsDic[hotKeySetting] = HotKeyManager.GlobalAddAtom(hotKeySetting.ToString());
            }
            else
            {
                // 註銷舊的熱鍵
                HotKeyManager.UnregisterHotKey(hWnd, m_HotKeySettingsDic[hotKeySetting]);
            }

            if (!hotKeyModel.IsUsable)
                return true;

            // 註冊熱鍵
            if (hotKeyModel.IsSelectCtrl && !hotKeyModel.IsSelectShift && !hotKeyModel.IsSelectAlt)
            {
                fsModifierKey = ModifierKeys.Control;
            }
            else if (!hotKeyModel.IsSelectCtrl && hotKeyModel.IsSelectShift && !hotKeyModel.IsSelectAlt)
            {
                fsModifierKey = ModifierKeys.Shift;
            }
            else if (!hotKeyModel.IsSelectCtrl && !hotKeyModel.IsSelectShift && hotKeyModel.IsSelectAlt)
            {
                fsModifierKey = ModifierKeys.Alt;
            }
            else if (hotKeyModel.IsSelectCtrl && hotKeyModel.IsSelectShift && !hotKeyModel.IsSelectAlt)
            {
                fsModifierKey = ModifierKeys.Control | ModifierKeys.Shift;
            }
            else if (hotKeyModel.IsSelectCtrl && !hotKeyModel.IsSelectShift && hotKeyModel.IsSelectAlt)
            {
                fsModifierKey = ModifierKeys.Control | ModifierKeys.Alt;
            }
            else if (!hotKeyModel.IsSelectCtrl && hotKeyModel.IsSelectShift && hotKeyModel.IsSelectAlt)
            {
                fsModifierKey = ModifierKeys.Shift | ModifierKeys.Alt;
            }
            else if (hotKeyModel.IsSelectCtrl && hotKeyModel.IsSelectShift && hotKeyModel.IsSelectAlt)
            {
                fsModifierKey = ModifierKeys.Control | ModifierKeys.Shift | ModifierKeys.Alt;
            }
            return HotKeyManager.RegisterHotKey(hWnd, m_HotKeySettingsDic[hotKeySetting], fsModifierKey, (int)hotKeyModel.SelectKey);
        }
    }

 

快捷鍵觸發 


 

    經過判斷msg 是否爲WM_HOTKEY來判斷當前快捷鍵是否觸發。經過附加參數wideParam得到當前快捷鍵觸發的項,進而進行相應處理。另外,當前消息處理完成後須要將handled置爲true

        /// <summary>
        /// 窗體回調函數,接收全部窗體消息的事件處理函數
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <param name="msg">消息</param>
        /// <param name="wideParam">附加參數1</param>
        /// <param name="longParam">附加參數2</param>
        /// <param name="handled">是否處理</param>
        /// <returns>返回句柄</returns>
        private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wideParam, IntPtr longParam, ref bool handled)
        {
            var hotkeySetting = new EHotKeySetting();
            switch (msg)
            {
                case HotKeyManager.WM_HOTKEY:
                    int sid = wideParam.ToInt32();
                    if (sid == m_HotKeySettings[EHotKeySetting.全屏])
                    {
                        hotkeySetting = EHotKeySetting.全屏;
                        //TODO 執行全屏操做
                    }
                    else if (sid == m_HotKeySettings[EHotKeySetting.截圖])
                    {
                        hotkeySetting = EHotKeySetting.截圖;
                        //TODO 執行截圖操做
                    }
                    else if (sid == m_HotKeySettings[EHotKeySetting.播放])
                    {
                        hotkeySetting = EHotKeySetting.播放;
                        //TODO ......
                    }
                    else if (sid == m_HotKeySettings[EHotKeySetting.前進])
                    {
                        hotkeySetting = EHotKeySetting.前進;
                    }
                    else if (sid == m_HotKeySettings[EHotKeySetting.後退])
                    {
                        hotkeySetting = EHotKeySetting.後退;
                    }
                    else if (sid == m_HotKeySettings[EHotKeySetting.保存])
                    {
                        hotkeySetting = EHotKeySetting.保存;
                    }
                    else if (sid == m_HotKeySettings[EHotKeySetting.打開])
                    {
                        hotkeySetting = EHotKeySetting.打開;
                    }
                    else if (sid == m_HotKeySettings[EHotKeySetting.新建])
                    {
                        hotkeySetting = EHotKeySetting.新建;
                    }
                    else if (sid == m_HotKeySettings[EHotKeySetting.刪除])
                    {
                        hotkeySetting = EHotKeySetting.刪除;
                    }
MessageBox.Show(
string.Format("觸發【{0}】快捷鍵", hotkeySetting.ToString())); handled = true; break; } return IntPtr.Zero; }

 

 

源碼下載                                                                          移步到開始↑

相關文章
相關標籤/搜索