Skip to content

Commit e987343

Browse files
committed
handle F1 key
1 parent e510c4a commit e987343

File tree

4 files changed

+285
-10
lines changed

4 files changed

+285
-10
lines changed

sources/RevitDBExplorer/Domain/CHMService.cs

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ namespace RevitDBExplorer.Domain
99
internal class CHMService
1010
{
1111
public static void OpenCHM(SnoopableMember snoopableMember)
12-
{
13-
string helpFileName = AppSettings.Default.RevitAPICHMFilePath;
14-
helpFileName = helpFileName.Replace("\"", "");
15-
if (System.IO.File.Exists(helpFileName))
12+
{
13+
var helpFileName = GetCHMFilePath();
14+
if (helpFileName != null)
1615
{
1716
string postfix = "";
1817
switch (snoopableMember.MemberKind)
@@ -30,11 +29,44 @@ public static void OpenCHM(SnoopableMember snoopableMember)
3029
System.Windows.Forms.Help.ShowHelp(null, helpFileName,
3130
System.Windows.Forms.HelpNavigator.KeywordIndex,
3231
$"{snoopableMember.DeclaringType.BareName}.{snoopableMember.Name}{postfix}");
32+
}
33+
}
34+
35+
public static void OpenCHM()
36+
{
37+
var helpFileName = GetCHMFilePath();
38+
if (helpFileName != null)
39+
{
40+
System.Windows.Forms.Help.ShowHelp(null, helpFileName,
41+
System.Windows.Forms.HelpNavigator.Index,
42+
null
43+
);
44+
}
45+
}
46+
public static void OpenCHM(string keyword)
47+
{
48+
var helpFileName = GetCHMFilePath();
49+
if (helpFileName != null)
50+
{
51+
System.Windows.Forms.Help.ShowHelp(null, helpFileName,
52+
System.Windows.Forms.HelpNavigator.KeywordIndex,
53+
keyword);
54+
}
55+
}
56+
57+
private static string GetCHMFilePath()
58+
{
59+
string helpFileName = AppSettings.Default.RevitAPICHMFilePath;
60+
helpFileName = helpFileName.Replace("\"", "");
61+
if (System.IO.File.Exists(helpFileName))
62+
{
63+
return helpFileName;
3364
}
3465
else
3566
{
3667
MessageBox.Show($".chm file does not exist at the given location: {helpFileName}. Please set the correct location in the configuration.");
37-
}
68+
}
69+
return null;
3870
}
3971
}
4072
}

sources/RevitDBExplorer/MainWindow.xaml.cs

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ internal partial class MainWindow : Window, IAmWindowOpener, INotifyPropertyChan
3636
private readonly QueryVisualizationVM queryVisualizationVM;
3737
private readonly WorkspacesViewModel workspacesVM;
3838
private readonly DispatcherTimer isRevitBusyDispatcher;
39-
private readonly IRDV3DController rdvController;
39+
private readonly IRDV3DController rdvController;
40+
private readonly GlobalKeyboardHook globalKeyboardHook;
4041
private bool isRevitBusy;
4142
private bool isNewVerAvailable;
4243
private string newVersionLink;
@@ -152,8 +153,12 @@ public MainWindow()
152153
isRevitBusyDispatcher = new DispatcherTimer(TimeSpan.FromMilliseconds(500), DispatcherPriority.Background, IsRevitBusyDispatcher_Tick, Dispatcher.CurrentDispatcher);
153154

154155
workspacesVM.SelectedItemsChanged += Workspaces_SelectedItemChanged;
156+
157+
globalKeyboardHook = new GlobalKeyboardHook();
158+
globalKeyboardHook.KeyDown += GlobalKeyboardHook_KeyDown;
155159
}
156-
160+
161+
157162

158163
private async Task InitializeAsync()
159164
{
@@ -293,6 +298,7 @@ private void OpenRDS()
293298

294299
private void Window_Closed(object sender, EventArgs e)
295300
{
301+
globalKeyboardHook.unhook();
296302
rdvController.Dispose();
297303
//Application.RevitWindowHandle.BringWindowToFront();
298304
Dispatcher.UnhandledException -= Dispatcher_UnhandledException;
@@ -317,7 +323,34 @@ private void Window_KeyDown(object sender, KeyEventArgs e)
317323
//Application.RevitWindowHandle.PostKeyMessage(vkey);
318324
}
319325
}
320-
326+
private void GlobalKeyboardHook_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
327+
{
328+
if (e.KeyValue == (int)System.Windows.Forms.Keys.F1)
329+
{
330+
if (IsActive)
331+
{
332+
e.Handled = true;
333+
var selectedItems = Workspaces.GetSelectedItems();
334+
var listItemForMember = selectedItems.OfType<ListItemForMember>().FirstOrDefault();
335+
var snoopableObjectTreeItem = selectedItems.OfType<SnoopableObjectTreeItem>().FirstOrDefault();
336+
if (listItemForMember != null)
337+
{
338+
CHMService.OpenCHM(listItemForMember[0]);
339+
return;
340+
}
341+
if (snoopableObjectTreeItem != null)
342+
{
343+
var key = snoopableObjectTreeItem.Object.TypeName;
344+
CHMService.OpenCHM(key);
345+
return;
346+
}
347+
348+
CHMService.OpenCHM();
349+
}
350+
}
351+
}
352+
353+
321354

322355
private DispatcherTimer window_SizeChanged_Debouncer;
323356
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
@@ -350,6 +383,9 @@ protected virtual void OnPropertyChanged([CallerMemberName] String propertyName
350383
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
351384
}
352385

353-
#endregion
386+
387+
#endregion
388+
389+
354390
}
355391
}

sources/RevitDBExplorer/UIComponents/List/ListView.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434

3535
<ContextMenu x:Key="ContextMenuForNameColumn">
3636
<MenuItem Header="Copy" Command="{Binding Data.CopyNameCommand, Source={StaticResource Proxy}}" CommandParameter="{Binding}" />
37-
<MenuItem Header="Open RevitAPI.chm" Command="{Binding Data.OpenCHMCommand, Source={StaticResource Proxy}}" CommandParameter="{Binding}" />
37+
<MenuItem Header="Open RevitAPI.chm" InputGestureText="F1" Command="{Binding Data.OpenCHMCommand, Source={StaticResource Proxy}}" CommandParameter="{Binding}" />
3838
</ContextMenu>
3939
<ContextMenu x:Key="ContextMenuForValueColumn">
4040
<MenuItem Header="Copy" Command="{Binding Data.CopyValueCommand, Source={StaticResource Proxy}}" CommandParameter="{Binding}"/>
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Runtime.InteropServices;
4+
using System.Windows.Forms;
5+
using static RevitDBExplorer.WPF.GlobalKeyboardHook;
6+
7+
namespace RevitDBExplorer.WPF
8+
{
9+
/// <summary>
10+
/// A class that manages a global low level keyboard hook
11+
/// source: https://github.com/jparnell8839/globalKeyboardHook
12+
/// </summary>
13+
internal sealed class GlobalKeyboardHook
14+
{
15+
#region Constant, Structure and Delegate Definitions
16+
/// <summary>
17+
/// defines the callback type for the hook
18+
/// </summary>
19+
public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);
20+
21+
public struct keyboardHookStruct
22+
{
23+
public int vkCode;
24+
public int scanCode;
25+
public int flags;
26+
public int time;
27+
public int dwExtraInfo;
28+
}
29+
30+
const int WH_KEYBOARD_LL = 13;
31+
const int WM_KEYDOWN = 0x100;
32+
const int WM_KEYUP = 0x101;
33+
const int WM_SYSKEYDOWN = 0x104;
34+
const int WM_SYSKEYUP = 0x105;
35+
36+
//Modifier key vkCode constants
37+
private const int VK_SHIFT = 0x10;
38+
private const int VK_CONTROL = 0x11;
39+
private const int VK_MENU = 0x12;
40+
private const int VK_CAPITAL = 0x14;
41+
#endregion
42+
43+
#region Instance Variables
44+
/// <summary>
45+
/// The collections of keys to watch for
46+
/// </summary>
47+
public List<Keys> HookedKeys = new List<Keys>();
48+
/// <summary>
49+
/// Handle to the hook, need this to unhook and call the next hook
50+
/// </summary>
51+
IntPtr hhook = IntPtr.Zero;
52+
#endregion
53+
54+
#region Events
55+
/// <summary>
56+
/// Occurs when one of the hooked keys is pressed
57+
/// </summary>
58+
public event KeyEventHandler KeyDown;
59+
/// <summary>
60+
/// Occurs when one of the hooked keys is released
61+
/// </summary>
62+
public event KeyEventHandler KeyUp;
63+
#endregion
64+
65+
#region Constructors and Destructors
66+
/// <summary>
67+
/// Initializes a new instance of the <see cref="globalKeyboardHook"/> class and installs the keyboard hook.
68+
/// </summary>
69+
public GlobalKeyboardHook()
70+
{
71+
hookProcDel = hookProc;
72+
hook();
73+
}
74+
75+
/// <summary>
76+
/// Releases unmanaged resources and performs other cleanup operations before the
77+
/// <see cref="globalKeyboardHook"/> is reclaimed by garbage collection and uninstalls the keyboard hook.
78+
/// </summary>
79+
~GlobalKeyboardHook()
80+
{
81+
unhook();
82+
}
83+
#endregion
84+
85+
#region Public Methods
86+
/// <summary>
87+
/// Installs the global hook
88+
/// </summary>
89+
public void hook()
90+
{
91+
IntPtr hInstance = LoadLibrary("User32");
92+
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProcDel, hInstance, 0);
93+
}
94+
95+
private keyboardHookProc hookProcDel;
96+
/// <summary>
97+
/// Uninstalls the global hook
98+
/// </summary>
99+
public void unhook()
100+
{
101+
UnhookWindowsHookEx(hhook);
102+
}
103+
104+
/// <summary>
105+
/// The callback for the keyboard hook
106+
/// </summary>
107+
/// <param name="code">The hook code, if it isn't >= 0, the function shouldn't do anyting</param>
108+
/// <param name="wParam">The event type</param>
109+
/// <param name="lParam">The keyhook event information</param>
110+
/// <returns></returns>
111+
public int hookProc(int code, int wParam, ref keyboardHookStruct lParam)
112+
{
113+
if (code >= 0)
114+
{
115+
Keys key = (Keys)lParam.vkCode;
116+
//if (HookedKeys.Contains(key))
117+
{
118+
// Get Modifiers
119+
key = AddModifiers(key);
120+
KeyEventArgs kea = new KeyEventArgs(key);
121+
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null))
122+
{
123+
KeyDown(this, kea);
124+
}
125+
else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null))
126+
{
127+
KeyUp(this, kea);
128+
}
129+
if (kea.Handled)
130+
return 1;
131+
}
132+
}
133+
return CallNextHookEx(hhook, code, wParam, ref lParam);
134+
}
135+
136+
/// <summary>
137+
/// Checks whether Alt, Shift, Control or CapsLock
138+
/// is pressed at the same time as the hooked key.
139+
/// Modifies the keyCode to include the pressed keys.
140+
/// </summary>
141+
private Keys AddModifiers(Keys key)
142+
{
143+
//CapsLock
144+
if ((GetKeyState(VK_CAPITAL) & 0x0001) != 0) key = key | Keys.CapsLock;
145+
146+
//Shift
147+
if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) key = key | Keys.Shift;
148+
149+
//Ctrl
150+
if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) key = key | Keys.Control;
151+
152+
//Alt
153+
if ((GetKeyState(VK_MENU) & 0x8000) != 0) key = key | Keys.Alt;
154+
155+
return key;
156+
}
157+
#endregion
158+
159+
#region DLL imports
160+
/// <summary>
161+
/// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null
162+
/// </summary>
163+
/// <param name="idHook">The id of the event you want to hook</param>
164+
/// <param name="callback">The callback.</param>
165+
/// <param name="hInstance">The handle you want to attach the event to, can be null</param>
166+
/// <param name="threadId">The thread you want to attach the event to, can be null</param>
167+
/// <returns>a handle to the desired hook</returns>
168+
[DllImport("user32.dll")]
169+
static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);
170+
171+
/// <summary>
172+
/// Unhooks the windows hook.
173+
/// </summary>
174+
/// <param name="hInstance">The hook handle that was returned from SetWindowsHookEx</param>
175+
/// <returns>True if successful, false otherwise</returns>
176+
[DllImport("user32.dll")]
177+
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
178+
179+
/// <summary>
180+
/// Calls the next hook.
181+
/// </summary>
182+
/// <param name="idHook">The hook id</param>
183+
/// <param name="nCode">The hook code</param>
184+
/// <param name="wParam">The wparam.</param>
185+
/// <param name="lParam">The lparam.</param>
186+
/// <returns></returns>
187+
[DllImport("user32.dll")]
188+
static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam);
189+
190+
/// <summary>
191+
/// Loads the library.
192+
/// </summary>
193+
/// <param name="lpFileName">Name of the library</param>
194+
/// <returns>A handle to the library</returns>
195+
[DllImport("kernel32.dll")]
196+
static extern IntPtr LoadLibrary(string lpFileName);
197+
198+
/// <summary>
199+
/// Gets the state of modifier keys for a given keycode.
200+
/// </summary>
201+
/// <param name="keyCode">The keyCode</param>
202+
/// <returns></returns>
203+
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
204+
public static extern short GetKeyState(int keyCode);
205+
#endregion
206+
}
207+
}

0 commit comments

Comments
 (0)