[C#/WPF/.NET8] 키보드 후킹을 사용해 WIN + C 키를 누를 경우 메모장 실행하기
■ 키보드 후킹을 사용해 WIN + C 키를 누를 경우 메모장을 실행하는 방법을 보여준다. ※ 윈도우즈 10 버전에서 코타나 실행도 방지한다. ▶
■ 키보드 후킹을 사용해 WIN + C 키를 누를 경우 메모장을 실행하는 방법을 보여준다. ※ 윈도우즈 10 버전에서 코타나 실행도 방지한다. ▶
■ 가상 키보드를 사용하는 방법을 보여준다. ▶ HookType.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
namespace TestProject { /// <summary> /// 후킹 타입 /// </summary> public enum HookType : int { /// <summary> /// WH_JOURNALRECORD /// </summary> WH_JOURNALRECORD = 0, /// <summary> /// WH_JOURNALPLAYBACK /// </summary> WH_JOURNALPLAYBACK = 1, /// <summary> /// WH_KEYBOARD /// </summary> WH_KEYBOARD = 2, /// <summary> /// WH_GETMESSAGE /// </summary> WH_GETMESSAGE = 3, /// <summary> /// WH_CALLWNDPROC /// </summary> WH_CALLWNDPROC = 4, /// <summary> /// WH_CBT /// </summary> WH_CBT = 5, /// <summary> /// WH_SYSMSGFILTER /// </summary> WH_SYSMSGFILTER = 6, /// <summary> /// WH_MOUSE /// </summary> WH_MOUSE = 7, /// <summary> /// WH_HARDWARE /// </summary> WH_HARDWARE = 8, /// <summary> /// WH_DEBUG /// </summary> WH_DEBUG = 9, /// <summary> /// WH_SHELL /// </summary> WH_SHELL = 10, /// <summary> /// WH_FOREGROUNDIDLE /// </summary> WH_FOREGROUNDIDLE = 11, /// <summary> /// WH_CALLWNDPROCRET /// </summary> WH_CALLWNDPROCRET = 12, /// <summary> /// WH_KEYBOARD_LL /// </summary> WH_KEYBOARD_LL = 13, /// <summary> /// WH_MOUSE_LL /// </summary> WH_MOUSE_LL = 14 } } |
▶ KeyboardHookStructureFlag.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
namespace TestProject { /// <summary> /// 키보드 후킹 구조체 플래그 /// </summary> public enum KeyboardHookStructureFlag { /// <summary> /// LLKHF_EXTENDED /// </summary> LLKHF_EXTENDED = 0x01, /// <summary> /// LLKHF_INJECTED /// </summary> LLKHF_INJECTED = 0x10, /// <summary> /// LLKHF_ALTDOWN /// </summary> LLKHF_ALTDOWN = 0x20, /// <summary> /// LLKHF_UP /// </summary> LLKHF_UP = 0x80 } } |
▶ MouseMessage.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
namespace TestProject { /// <summary> /// 마우스 메시지 /// </summary> public enum MouseMessage { /// <summary> /// WM_LBUTTONDOWN /// </summary> WM_LBUTTONDOWN = 0x0201, /// <summary> /// WM_LBUTTONUP /// </summary> WM_LBUTTONUP = 0x0202, /// <summary> /// WM_MOUSEMOVE /// </summary> WM_MOUSEMOVE = 0x0200, /// <summary> /// WM_MOUSEWHEEL /// </summary> WM_MOUSEWHEEL = 0x020A, /// <summary> /// WM_RBUTTONDOWN /// </summary> WM_RBUTTONDOWN = 0x0204, /// <summary> /// WM_RBUTTONUP /// </summary> WM_RBUTTONUP = 0x0205, /// <summary> /// WM_NCLBUTTONDOWN /// </summary> WM_NCLBUTTONDOWN = 0x00A } } |
▶ POINT.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
using System.Runtime.InteropServices; namespace TestProject { /// <summary> /// 포인트 /// </summary> [StructLayout(LayoutKind.Sequential)] public struct POINT { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Public #region Field /// <summary> /// X /// </summary> public int X; /// <summary> /// Y /// </summary> public int Y; #endregion } } |
▶ KEYBOARDHOOKSTRUCT.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
using System; using System.Runtime.InteropServices; namespace TestProject { /// <summary> /// 키보드 후킹 구조체 /// </summary> [StructLayout(LayoutKind.Sequential)] public class KEYBOARDHOOKSTRUCT { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Public #region Field /// <summary> /// 가상 키 코드 /// </summary> public uint VirtualKeyCode; /// <summary> /// 스캔 코드 /// </summary> public uint ScanCode; /// <summary> /// 키보드 후킹 구조체 플래그 /// </summary> public KeyboardHookStructureFlag Flag; /// <summary> /// 시간 /// </summary> public uint Time; /// <summary> /// 부가 정보 핸들 /// </summary> public UIntPtr ExtraInformationHandle; #endregion } } |
▶
■ TextBox 엘리먼트의 ContextMenu 속성을 사용해 커스텀 컨텍스트 메뉴를 만드는 방법을 보여준다. ▶ MainWindow.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<Window x:Class="TestProject.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="800" Height="600" Title="TestProject" FontFamily="나눔고딕코딩" FontSize="16"> <TextBox Name="textBox" Margin="10" AcceptsReturn="True" AcceptsTab="True" VerticalScrollBarVisibility="Visible" TextWrapping="Wrap"> <TextBox.ContextMenu> <ContextMenu Name="contextMenu"> <MenuItem Name="cutMenuItem" Header="Cut" /> <MenuItem Name="copyMenuItem" Header="Copy" /> <MenuItem Name="pasteMenuItem" Header="Paste" /> <Separator/> <MenuItem Name="selectAllMenuItem" Header="Select All" /> <MenuItem Name="selectLineMenuItem" Header="Select Current Line" /> <Separator /> <MenuItem Name="undoMenuItem" Header="Undo Last Action" /> <MenuItem Name="redoMenuItem" Header="Redo Last Action" /> <Separator /> <MenuItem Name="clearMenuItem" Header="Clear All Text" /> </ContextMenu> </TextBox.ContextMenu> This TextBox uses a simple custom context menu. The context menu can be disabled by checking the CheckBox above, which simply sets the TextBox.ContextMenu property to null. </TextBox> </Window> |
▶ MainWindow.xaml.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
using System.Windows; namespace TestProject { /// <summary> /// 메인 윈도우 /// </summary> public partial class MainWindow : Window { //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - MainWindow() /// <summary> /// 생성자 /// </summary> public MainWindow() { InitializeComponent(); this.contextMenu.Opened += contextMenu_Opened; this.cutMenuItem.Click += cutMenuItem_Click; this.copyMenuItem.Click += copyMenuItem_Click; this.pasteMenuItem.Click += pasteMenuItem_Click; this.selectAllMenuItem.Click += selectAllMenuItem_Click; this.selectLineMenuItem.Click += selectLineMenuItem_Click; this.undoMenuItem.Click += undoMenuItem_Click; this.redoMenuItem.Click += redoMenuItem_Click; this.clearMenuItem.Click += clearMenuItem_Click; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 컨텍스트 메뉴 오픈시 처리하기 - contextMenu_Opened(sender, e) /// <summary> /// 컨텍스트 메뉴 오픈시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void contextMenu_Opened(object sender, RoutedEventArgs e) { if(this.textBox.SelectedText == string.Empty) { this.copyMenuItem.IsEnabled = this.cutMenuItem.IsEnabled = false; } else { this.copyMenuItem.IsEnabled = this.cutMenuItem.IsEnabled = true; } if(Clipboard.ContainsText()) { this.pasteMenuItem.IsEnabled = true; } else { this.pasteMenuItem.IsEnabled = false; } } #endregion #region Cut 메뉴 항목 클릭시 처리하기 - cutMenuItem_Click(sender, e) /// <summary> /// Cut 메뉴 항목 클릭시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void cutMenuItem_Click(object sender, RoutedEventArgs e) { this.textBox.Cut(); } #endregion #region Copy 메뉴 항목 클릭시 처리하기 - copyMenuItem_Click(sender, e) /// <summary> /// Copy 메뉴 항목 클릭시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void copyMenuItem_Click(object sender, RoutedEventArgs e) { this.textBox.Copy(); } #endregion #region Paste 메뉴 항목 클릭시 처리하기 - pasteMenuItem_Click(sender, e) /// <summary> /// Paste 메뉴 항목 클릭시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void pasteMenuItem_Click(object sender, RoutedEventArgs e) { this.textBox.Paste(); } #endregion #region Select All 메뉴 항목 클릭시 처리하기 - selectAllMenuItem_Click(sender, e) /// <summary> /// Select All 메뉴 항목 클릭시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void selectAllMenuItem_Click(object sender, RoutedEventArgs e) { this.textBox.SelectAll(); } #endregion #region Select Line 메뉴 항목 클릭시 처리하기 - selectLineMenuItem_Click(sender, e) /// <summary> /// Select Line 메뉴 항목 클릭시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void selectLineMenuItem_Click(object sender, RoutedEventArgs e) { int lineIndex = this.textBox.GetLineIndexFromCharacterIndex(this.textBox.CaretIndex); int characterIndex = this.textBox.GetCharacterIndexFromLineIndex(lineIndex); int lineLength = this.textBox.GetLineLength(lineIndex); this.textBox.Select(characterIndex, lineLength); } #endregion #region Undo 메뉴 항목 클릭시 처리하기 - undoMenuItem_Click(sender, e) /// <summary> /// Undo 메뉴 항목 클릭시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void undoMenuItem_Click(object sender, RoutedEventArgs e) { this.textBox.Undo(); } #endregion #region Redo 메뉴 항목 클릭시 처리하기 - redoMenuItem_Click(sender, e) /// <summary> /// Redo 메뉴 항목 클릭시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void redoMenuItem_Click(object sender, RoutedEventArgs e) { this.textBox.Redo(); } #endregion #region Clear 메뉴 항목 클릭시 처리하기 - clearMenuItem_Click(sender, e) /// <summary> /// Clear 메뉴 항목 클릭시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void clearMenuItem_Click(object sender, RoutedEventArgs e) { this.textBox.Clear(); } #endregion } } |
TestProject.zip
■ 22.04 버전 우분투에서 한글 입력 모드 전환을 설정하는 방법을 보여준다. 1. 아래와 같이 우상단을 클릭한다. 2. 아래와 같이 [설정] 메뉴를 클릭한다.
■ Application 클래스의 On<T> 메소드를 사용해 소프트 키보드 입력 모드를 설정하는 방법을 보여준다. ▶ App.xaml
1 2 3 4 5 6 |
<?xml version = "1.0" encoding = "UTF-8" ?> <Application x:Class="TestProject.App" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" /> |
▶ App.xaml.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
using Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific; namespace TestProject; /// <summary> /// 앱 /// </summary> public partial class App : Microsoft.Maui.Controls.Application { //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - App() /// <summary> /// 생성자 /// </summary> public App() { InitializeComponent(); MainPage = new MainPage(); App.Current.On<Microsoft.Maui.Controls.PlatformConfiguration.Android>().UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize); } #endregion } |
※ 프리뷰 버전
■ Application 엘리먼트의 WindowSoftInputModeAdjust 첨부 속성을 사용해 소프트 키보드 입력 모드를 설정하는 방법을 보여준다. ▶ App.xaml
1 2 3 4 5 6 7 8 |
<?xml version = "1.0" encoding = "UTF-8" ?> <Application x:Class="TestProject.App" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:android="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;assembly=Microsoft.Maui.Controls" android:Application.WindowSoftInputModeAdjust="Resize" /> |
※ 프리뷰 버전 테스트시, 설정
■ Entry 클래스의 On<T> 메소드를 사용해 소프트 키보드 사용자 작업 버튼을 설정하는 방법을 보여준다. (ANDROID) ▶ MainPage.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="TestProject.MainPage" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"> <StackLayout HorizontalOptions="Center" VerticalOptions="Center" Spacing="10"> <StackLayout HorizontalOptions="Start" Orientation="Horizontal" Spacing="10"> <Label VerticalOptions="Center" Text="항목 1" /> <Entry x:Name="entry1" VerticalOptions="Center" MinimumWidthRequest="100" Placeholder="값 1" /> </StackLayout> <StackLayout HorizontalOptions="Start" Orientation="Horizontal" Spacing="10"> <Label VerticalOptions="Center" Text="항목 2" /> <Entry x:Name="entry2" VerticalOptions="Center" MinimumWidthRequest="100" Placeholder="값 2" /> </StackLayout> </StackLayout> </ContentPage> |
▶ MainPage.xaml.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
using Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific; namespace TestProject; /// <summary> /// 메인 페이지 /// </summary> public partial class MainPage : ContentPage { //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - MainPage() /// <summary> /// 생성자 /// </summary> public MainPage() { InitializeComponent(); Loaded += ContentPage_Loaded; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Private #region 컨텐트 페이지 로드시 처리하기 - ContentPage_Loaded(sender, e) /// <summary> /// 컨텐트 페이지 로드시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void ContentPage_Loaded(object sender, EventArgs e) { // 생성자에서 실행하면 적용이 안된다. this.entry1.On<Microsoft.Maui.Controls.PlatformConfiguration.Android>().SetImeOptions(ImeFlags.Send); this.entry2.On<Microsoft.Maui.Controls.PlatformConfiguration.Android>().SetImeOptions(ImeFlags.Go ); } #endregion } |
TestProject.zip
■ Entry 엘리먼트의 ImeOptions 첨부 속성을 사용해 소프트 키보드 사용자 작업 버튼을 설정하는 방법을 보여준다. (ANDROID) ▶ MainPage.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="TestProject.MainPage" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:android="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;assembly=Microsoft.Maui.Controls"> <StackLayout HorizontalOptions="Center" VerticalOptions="Center" Spacing="10"> <StackLayout HorizontalOptions="Start" Orientation="Horizontal" Spacing="10"> <Label VerticalOptions="Center" Text="항목 1" /> <Entry VerticalOptions="Center" MinimumWidthRequest="100" android:Entry.ImeOptions="Send" Placeholder="값 1" /> </StackLayout> <StackLayout HorizontalOptions="Start" Orientation="Horizontal" Spacing="10"> <Label VerticalOptions="Center" Text="항목 2" /> <Entry VerticalOptions="Center" MinimumWidthRequest="100" android:Entry.ImeOptions="Go" Placeholder="값 2" /> </StackLayout> </StackLayout> </ContentPage> |
TestProject.zip
■ 저수준 키보드 후킹을 하는 방법을 보여준다. ▶ LowLevelKeyboardHook.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; namespace TestProject { /// <summary> /// 저수준 키보드 후킹 /// </summary> public class LowLevelKeyboardHook { //////////////////////////////////////////////////////////////////////////////////////////////////// Delegate ////////////////////////////////////////////////////////////////////////////////////////// Private #region 저수준 키보드 대리자 - LowLevelKeyboardDelegate(code, wordParameter, longParameter) /// <summary> /// 저수준 키보드 대리자 /// </summary> /// <param name="code">코드</param> /// <param name="wordParameter">WORD 매개 변수</param> /// <param name="longParameter">LONG 매개 변수</param> /// <returns></returns> private delegate IntPtr LowLevelKeyboardDelegate(int code, IntPtr wordParameter, IntPtr longParameter); #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Import ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region 모듈 핸들 구하기 - GetModuleHandle(moduleName) /// <summary> /// 모듈 핸들 구하기 /// </summary> /// <param name="moduleName">모듈명</param> /// <returns>모듈 핸들</returns> [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr GetModuleHandle(string moduleName); #endregion #region 윈도우즈 후킹 설정하기 (확장) - SetWindowsHookEx(hookType, callback, moduleHandle, threadID) /// <summary> /// 윈도우즈 후킹 설정하기 (확장) /// </summary> /// <param name="hookType">후킹 타입</param> /// <param name="callback">콜백 함수</param> /// <param name="moduleHandle">모듈 핸들</param> /// <param name="threadID">쓰레드 ID</param> /// <returns>후킹 ID</returns> [DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr SetWindowsHookEx(int hookType, LowLevelKeyboardDelegate callback, IntPtr moduleHandle, uint threadID); #endregion #region 다음 후킹 호출하기 (확장) - CallNextHookEx(hookID, code, wordParameter, longParameter) /// <summary> /// 다음 후킹 호출하기 (확장) /// </summary> /// <param name="hookID">후킹 ID</param> /// <param name="code">코드</param> /// <param name="wordParameter">WORD 매개 변수</param> /// <param name="longParameter">LONG 매개 변수</param> /// <returns>후킹 ID</returns> [DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr CallNextHookEx(IntPtr hookID, int code, IntPtr wordParameter, IntPtr longParameter); #endregion #region 윈도우즈 후킹 취소하기 (확장) - UnhookWindowsHookEx(hookID) /// <summary> /// 윈도우즈 후킹 취소하기 (확장) /// </summary> /// <param name="hookID">후킹 ID</param> /// <returns>처리 결과</returns> [DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool UnhookWindowsHookEx(IntPtr hookID); #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Event ////////////////////////////////////////////////////////////////////////////////////////// Public #region 키 PRESSED 이벤트 - KeyPressed /// <summary> /// 키 PRESSED 이벤트 /// </summary> public event EventHandler<Keys> KeyPressed; #endregion #region 키 UNPRESSED 이벤트 - KeyUnpressed /// <summary> /// 키 UNPRESSED 이벤트 /// </summary> public event EventHandler<Keys> KeyUnpressed; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// WH_KEYBOARD_LL /// </summary> private const int WH_KEYBOARD_LL = 13; /// <summary> /// WM_KEYDOWN /// </summary> private const int WM_KEYDOWN = 0x0100; /// <summary> /// WM_SYSKEYDOWN /// </summary> private const int WM_SYSKEYDOWN = 0x0104; /// <summary> /// WM_KEYUP /// </summary> private const int WM_KEYUP = 0x101; /// <summary> /// WM_SYSKEYUP /// </summary> private const int WM_SYSKEYUP = 0x105; /// <summary> /// 저수준 키보드 대리자 /// </summary> private LowLevelKeyboardDelegate lowLevelKeyboardDelegate; /// <summary> /// 후킹 ID /// </summary> private IntPtr hookID = IntPtr.Zero; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - LowLevelKeyboardHook() /// <summary> /// 생성자 /// </summary> public LowLevelKeyboardHook() { this.lowLevelKeyboardDelegate = ProcessLowLevelKeyboard; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 키보드 후킹하기 - HookKeyboard() /// <summary> /// 키보드 후킹하기 /// </summary> public void HookKeyboard() { this.hookID = SetHook(this.lowLevelKeyboardDelegate); } #endregion #region 키보드 후킹 취소하기 - UnHookKeyboard() /// <summary> /// 키보드 후킹 취소하기 /// </summary> public void UnHookKeyboard() { UnhookWindowsHookEx(this.hookID); } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Private #region 후킹 설정하기 - SetHook(lowLevelKeyboardDelegate) /// <summary> /// 후킹 설정하기 /// </summary> /// <param name="lowLevelKeyboardDelegate">저수준 키보드 대리자</param> /// <returns>후킹 ID</returns> private IntPtr SetHook(LowLevelKeyboardDelegate lowLevelKeyboardDelegate) { using(Process process = Process.GetCurrentProcess()) { using(ProcessModule processModule = process.MainModule) { return SetWindowsHookEx ( WH_KEYBOARD_LL, lowLevelKeyboardDelegate, GetModuleHandle(processModule.ModuleName), 0 ); } } } #endregion #region 저수준 키보드 처리하기 - ProcessLowLevelKeyboard(code, wordParameter, longParameter) /// <summary> /// 저수준 키보드 처리하기 /// </summary> /// <param name="code">코드</param> /// <param name="wordParameter">WORD 매개 변수</param> /// <param name="longParameter">LONG 매개 변수</param> /// <returns>처리 결과</returns> private IntPtr ProcessLowLevelKeyboard(int code, IntPtr wordParameter, IntPtr longParameter) { if(code >= 0 && wordParameter == (IntPtr)WM_KEYDOWN || wordParameter == (IntPtr)WM_SYSKEYDOWN) { int vkCode = Marshal.ReadInt32(longParameter); KeyPressed?.Invoke(this, ((Keys)vkCode)); } else if(code >= 0 && wordParameter == (IntPtr)WM_KEYUP || wordParameter == (IntPtr)WM_SYSKEYUP) { int vkCode = Marshal.ReadInt32(longParameter); KeyUnpressed?.Invoke(this, ((Keys)vkCode)); } return CallNextHookEx(hookID, code, wordParameter, longParameter); } #endregion } } |
▶ MainForm.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
using System.Windows.Forms; namespace TestProject { /// <summary> /// 메인 폼 /// </summary> public partial class MainForm : Form { //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - MainForm() /// <summary> /// 생성자 /// </summary> public MainForm() { InitializeComponent(); } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 메시지 표시하기 - DisplayMessage(keys) /// <summary> /// 메시지 표시하기 /// </summary> /// <param name="keys">키</param> public void DisplayMessage(Keys keys) { this.messageLabel.Text = keys.ToString(); } #endregion } } |
▶ Program.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
using System; using System.Windows.Forms; namespace TestProject { /// <summary> /// 프로그램 /// </summary> class Program { //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region 프로그램 시작하기 - Main() /// <summary> /// 프로그램 시작하기 /// </summary> [STAThread] private static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); MainForm mainForm = new MainForm(); LowLevelKeyboardHook keyboardHook = new LowLevelKeyboardHook(); keyboardHook.KeyPressed += (sender, e) => mainForm.DisplayMessage(e); keyboardHook.HookKeyboard(); Application.Run(mainForm); keyboardHook.UnHookKeyboard(); } #endregion } } |
TestProject.zip
■ InputMethod 엘리먼트의 PreferredImeState/PreferredImeConversionMode 첨부 속성을 사용해 한글 모드에서 입력을 시작하는 방법을 보여준다. ▶ MainWindow.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<Window x:Class="TestProject.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="800" Height="600" Title="InputMethod 엘리먼트 : PreferredImeState/PreferredImeConversionMode 첨부 속성을 사용해 한글 모드에서 입력 시작하기" FontFamily="나눔고딕코딩" FontSize="16"> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <TextBlock HorizontalAlignment="Left" Text="한글 모드에서 입력 시작" /> <TextBox HorizontalAlignment="Left" Margin="0 10 0 0" Width="200" Height="25" InputMethod.PreferredImeState="On" InputMethod.PreferredImeConversionMode="Native" VerticalContentAlignment="Center" /> <TextBlock HorizontalAlignment="Left" Margin="0 10 0 0" Text="영문 모드에서 입력 시작" /> <TextBox HorizontalAlignment="Left" Margin="0 10 0 0" Width="200" Height="25" InputMethod.PreferredImeState="On" InputMethod.PreferredImeConversionMode="Alphanumeric" VerticalContentAlignment="Center" /> </StackPanel> </Window> |
TestProject.zip
■ Entry 엘리먼트의 Keyboard 속성을 사용하는 방법을 보여준다. ▶ MainPage.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="TestProject.MainPage" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"> <Entry HorizontalOptions="Center" VerticalOptions="Center" Placeholder="Enter text"> <Entry.Keyboard> <Keyboard x:FactoryMethod="Create"> <x:Arguments> <KeyboardFlags>Suggestions,CapitalizeCharacter</KeyboardFlags> </x:Arguments> </Keyboard> </Entry.Keyboard> </Entry> </ContentPage> |
TestProject.zip
■ Entry 엘리먼트의 Keyboard 속성을 사용하는 방법을 보여준다. ▶ MainPage.xaml
1 2 3 4 5 6 7 8 9 10 11 12 |
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="TestProject.MainPage" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"> <Entry HorizontalOptions="Center" VerticalOptions="Center" Keyboard="Chat" Placeholder="Enter text" /> </ContentPage> |
TestProject.zip
■ Editor 엘리먼트의 Keyboard 속성을 사용하는 방법을 보여준다. ▶ MainPage.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="TestProject.MainPage" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"> <Editor HorizontalOptions="Center" VerticalOptions="Center" AutoSize="TextChanges" Placeholder="Enter your response here"> <Editor.Keyboard> <Keyboard x:FactoryMethod="Create"> <x:Arguments> <KeyboardFlags>Suggestions,CapitalizeCharacter</KeyboardFlags> </x:Arguments> </Keyboard> </Editor.Keyboard> </Editor> </ContentPage> |
TestProject.zip
■ Editor 엘리먼트의 Keyboard 속성을 사용하는 방법을 보여준다. ▶ MainPage.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="TestProject.MainPage" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"> <Editor HorizontalOptions="Center" VerticalOptions="Center" AutoSize="TextChanges" Keyboard="Chat" Placeholder="Enter your response here" /> </ContentPage> |
TestProject.zip
■ Keyboard 엘리먼트에서 KeyboardFlags 열거형을 사용하는 방법을 보여준다. ▶ CatPage.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="TestProject.CatPage" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:TestProject" Title="Cat Page" BackgroundColor="White"> <Shell.SearchHandler> <local:AnimalSearchHandler DisplayMemberName="Name" Placeholder="동물명을 입력해 주시기 바랍니다."> <SearchHandler.Keyboard> <Keyboard x:FactoryMethod="Create"> <x:Arguments> <KeyboardFlags>Suggestions,CapitalizeCharacter</KeyboardFlags> </x:Arguments> </Keyboard> </SearchHandler.Keyboard> </local:AnimalSearchHandler> </Shell.SearchHandler> <StackLayout> <Label Text="CAT PAGE" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand" /> </StackLayout> </ContentPage> |
TestProject.zip
■ UIElement 클래스의 GotFocus/LostFocus 이벤트를 사용해 키보드 포커스 획득/상실시 처리하는 방법을 보여준다. ▶ MainWindow.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<Window x:Class="TestProject.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="800" Height="600" Title="UIElement 클래스 : GotFocus/LostFocus 이벤트를 사용해 키보드 포커스 획득/상실시 처리하기" FontFamily="나눔고딕코딩" FontSize="16"> <Grid> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <Button Name="button1" Width="100" Height="30"> 테스트 </Button> <Button Name="button2" Margin="0 10 0 0" Width="100" Height="30"> 테스트 </Button> </StackPanel> </Grid> </Window> |
▶ MainWindow.xaml.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
using System.Windows; using System.Windows.Controls; using System.Windows.Media; namespace TestProject { /// <summary> /// 메인 윈도우 /// </summary> public partial class MainWindow : Window { //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - MainWindow() /// <summary> /// 생성자 /// </summary> public MainWindow() { InitializeComponent(); this.button1.GotFocus += button_GotFocus; this.button1.LostFocus += button_LostFocus; this.button2.GotFocus += button_GotFocus; this.button2.LostFocus += button_LostFocus; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Private #region 버튼 포커스 획득시 처리하기 - button_GotFocus(sender, e) /// <summary> /// 버튼 포커스 획득시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void button_GotFocus(object sender, RoutedEventArgs e) { Button button = e.Source as Button; button.Background = Brushes.Red; } #endregion #region 버튼 포커스 상실시 처리하기 - button_LostFocus(sender, e) /// <summary> /// 버튼 포커스 상실시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void button_LostFocus(object sender, RoutedEventArgs e) { Button button = e.Source as Button; button.Background = Brushes.White; } #endregion } } |
TestProject.zip
■ 커스텀 포커스 범위를 사용하는 방법을 보여준다. ▶ CustomFocusScope.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
using System.Windows; using System.Windows.Input; using System.Windows.Media; namespace TestProject { /// <summary> /// 커스텀 포커스 범위 /// </summary> public static class CustomFocusScope { //////////////////////////////////////////////////////////////////////////////////////////////////// Dependency Property ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Public #region 커스텀 포커스 스코프 여부 첨부 속성 - IsCustomFocusScopeProperty /// <summary> /// 커스텀 포커스 스코프 여부 첨부 속성 /// </summary> public static readonly DependencyProperty IsCustomFocusScopeProperty = DependencyProperty.RegisterAttached ( "IsCustomFocusScope", typeof(bool), typeof(CustomFocusScope), new UIPropertyMetadata(false, IsCustomFocusScopePropertyChangedCallback) ); #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Public #region 커스텀 포커스 범위 여부 구하기 - GetIsCustomFocusScope(element) /// <summary> /// 커스텀 포커스 범위 여부 구하기 /// </summary> /// <param name="element">엘리먼트</param> /// <returns>커스텀 포커스 범위 여부</returns> public static bool GetIsCustomFocusScope(UIElement element) { return (bool)element.GetValue(IsCustomFocusScopeProperty); } #endregion #region 커스텀 포커스 범위 여부 설정하기 - SetIsCustomFocusScope(element, value) /// <summary> /// 커스텀 포커스 범위 여부 설정하기 /// </summary> /// <param name="element">엘리먼트</param> /// <param name="value">커스텀 포커스 범위 여부</param> public static void SetIsCustomFocusScope(UIElement element, bool value) { element.SetValue(IsCustomFocusScopeProperty, value); } #endregion #region 범위 내에서 활성 엘리먼트 포커스 설정하기 - SetFocusOnActiveElementInScope(focusScopeElement) /// <summary> /// 범위 내에서 활성 엘리먼트 포커스 설정하기 /// </summary> /// <param name="focusScopeElement">포커스 범위 엘리먼트</param> public static void SetFocusOnActiveElementInScope(UIElement focusScopeElement) { IInputElement focusedElement = FocusManager.GetFocusedElement(focusScopeElement); if(focusedElement != null) { Keyboard.Focus(focusedElement); } else { focusScopeElement.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); } } #endregion //////////////////////////////////////////////////////////////////////////////// Private #region 커스텀 포커스 범위 여부 속성 변경시 콜백 처리하기 - IsCustomFocusScopePropertyChangedCallback(d, e) /// <summary> /// 커스텀 포커스 범위 여부 속성 변경시 콜백 처리하기 /// </summary> /// <param name="d">의존 객체</param> /// <param name="e">이벤트 인자</param> private static void IsCustomFocusScopePropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) { UIElement element = d as UIElement; if(element == null) { return; } if((bool)e.NewValue) { FocusManager.SetIsFocusScope(element, true); element.GotKeyboardFocus += element_GotKeyboardFocus; } else { FocusManager.SetIsFocusScope(element, false); element.GotKeyboardFocus -= element_GotKeyboardFocus; } } #endregion #region 엘리먼트 키보드 포커스 획득시 처리하기 - element_GotKeyboardFocus(sender, e) /// <summary> /// 엘리먼트 키보드 포커스 획득시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private static void element_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) { IInputElement focusedElement = e.NewFocus; for(DependencyObject d = focusedElement as DependencyObject; d != null; d = VisualTreeHelper.GetParent(d)) { if(FocusManager.GetIsFocusScope(d)) { d.SetValue(FocusManager.FocusedElementProperty, focusedElement); if(!(bool)d.GetValue(IsCustomFocusScopeProperty)) { break; } } } } #endregion } } |
▶ MainWindow.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
<Window x:Class="TestProject.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:TestProject" Width="800" Height="600" Title="커스텀 포커스 범위 사용하기" FontFamily="나눔고딕코딩" FontSize="16"> <Window.Resources> <Style TargetType="GroupBox"> <Setter Property="Background" Value="Transparent" /> <Setter Property="KeyboardNavigation.TabNavigation" Value="Cycle" /> <Setter Property="KeyboardNavigation.ControlTabNavigation" Value="Once" /> </Style> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="150" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <ToolBar Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3"> <Button Margin="10 0 0 0" Command="Copy"> 복사 </Button> <Button Margin="10 0 0 0" Command="Cut"> 잘라내기 </Button> <Button Margin="10 0 0 0" Command="Paste"> 붙여넣기 </Button> </ToolBar> <TextBox Grid.Row="1" Grid.ColumnSpan="3" Margin="10" BorderThickness="1" BorderBrush="Black" Padding="10" AcceptsReturn="True"> A free text area </TextBox> <GroupBox Name="groupBox1" Grid.Row="2" Grid.Column="0" Header="포커스 범위 없음"> <StackPanel Margin="10"> <Button Height="30">A</Button> <Button Height="30">B</Button> <Button Height="30">C</Button> <TextBox Margin="0 10 0 0" Height="25" VerticalContentAlignment="Center" /> <CheckBox Margin="0 10 0 0">CheckBox</CheckBox> </StackPanel> </GroupBox> <GroupBox Name="groupBox2" Grid.Row="2" Grid.Column="1" Header="WPF 포커스 범위" FocusManager.IsFocusScope="True"> <StackPanel Margin="10"> <Button Height="30">A</Button> <Button Height="30">B</Button> <Button Height="30">C</Button> <TextBox Margin="0 10 0 0" Height="25" VerticalContentAlignment="Center" /> <CheckBox Margin="0 10 0 0">CheckBox</CheckBox> </StackPanel> </GroupBox> <GroupBox Name="groupBox3" Grid.Row="2" Grid.Column="2" Header="커스텀 포커스 범위" local:CustomFocusScope.IsCustomFocusScope="True"> <StackPanel Margin="10"> <Button Height="30">A</Button> <Button Height="30">B</Button> <Button Height="30">C</Button> <TextBox Margin="0 10 0 0" Height="25" VerticalContentAlignment="Center" /> <CheckBox Margin="0 10 0 0">CheckBox</CheckBox> </StackPanel> </GroupBox> </Grid> </Window> |
▶ MainWindow.xaml.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
using System.Reflection; using System.Windows; using System.Windows.Input; namespace TestProject { /// <summary> /// 메인 윈도우 /// </summary> public partial class MainWindow : Window { //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - MainWindow() /// <summary> /// 생성자 /// </summary> public MainWindow() { InitializeComponent(); this.groupBox1.MouseDown += groupBox1_MouseDown; this.groupBox2.MouseDown += groupBox2_MouseDown; this.groupBox3.MouseDown += groupBox3_MouseDown; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Private #region 그룹 박스 1 마우스 DOWN 처리하기 - groupBox1_MouseDown(sender, e) /// <summary> /// 그룹 박스 1 마우스 DOWN 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void groupBox1_MouseDown(object sender, MouseButtonEventArgs e) { object keyboardNavigation = typeof(KeyboardNavigation).InvokeMember ( "Current", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.GetProperty, null, null, null ); object activeElement = typeof(KeyboardNavigation).InvokeMember ( "GetActiveElement", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod, null, keyboardNavigation, new object[] { this.groupBox1 } ); IInputElement focusedElement = activeElement as IInputElement; if(focusedElement != null) { Keyboard.Focus(focusedElement); } else { this.groupBox1.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); } } #endregion #region 그룹 박스 2 마우스 DOWN 처리하기 - groupBox2_MouseDown(sender, e) /// <summary> /// 그룹 박스 2 마우스 DOWN 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void groupBox2_MouseDown(object sender, MouseButtonEventArgs e) { IInputElement focusedElement = FocusManager.GetFocusedElement(this.groupBox2); if(focusedElement != null) { Keyboard.Focus(focusedElement); } else { this.groupBox2.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); } } #endregion #region 그룹 박스 3 마우스 DOWN 처리하기 - groupBox3_MouseDown(sender, e) /// <summary> /// 그룹 박스 3 마우스 DOWN 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void groupBox3_MouseDown(object sender, MouseButtonEventArgs e) { CustomFocusScope.SetFocusOnActiveElementInScope(this.groupBox3); } #endregion } } |
TestProject.zip
■ FocusManager 클래스의 FocusedElement 첨부 속성을 사용해 포커스를 설정하는 방법을 보여준다. ▶ MainWindow.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<Window x:Class="TestProject.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="800" Height="600" Title="FocusManager 클래스 : FocusedElement 첨부 속성을 사용해 포커스 설정하기" FontFamily="나눔고딕코딩" FontSize="16" FocusManager.FocusedElement="{Binding ElementName=textBox2}"> <Grid HorizontalAlignment="Center" VerticalAlignment="Center"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Text="항목 1" /> <TextBox Name="textBox1" Grid.Row="0" Grid.Column="1" Margin="10 0 0 0" Width="200" Height="25" BorderThickness="1" BorderBrush="Black" VerticalContentAlignment="Center" /> <TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Margin="0 10 0 0" Text="항목 2" /> <TextBox Name="textBox2" Grid.Row="1" Grid.Column="1" Margin="10 10 0 0" Width="200" Height="25" BorderThickness="1" BorderBrush="Black" VerticalContentAlignment="Center" /> </Grid> </Window |
TestProject.zip
■ TextBox 클래스의 KeyDown 이벤트를 사용해 ENTER 키를 누르는 경우 포커스를 해제하는 방법을 보여준다. ▶ MainWindow.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<Window x:Class="TestProject.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="800" Height="600" Title="TextBox 클래스 : KeyDown 이벤트를 사용해 ENTER 키를 누르는 경우 포커스 해제하기" FontFamily="나눔고딕코딩" FontSize="16"> <Grid> <TextBox Name="textBox" Width="300" Height="25" BorderThickness="1" BorderBrush="Black" VerticalContentAlignment="Center" /> </Grid> </Window> |
▶ MainWindow.xaml.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
using System.Windows; using System.Windows.Input; namespace TestProject { /// <summary> /// 메인 윈도우 /// </summary> public partial class MainWindow : Window { //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - MainWindow() /// <summary> /// 생성자 /// </summary> public MainWindow() { InitializeComponent(); this.textBox.KeyDown += textBox_KeyDown; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Private //////////////////////////////////////////////////////////////////////////////// Event #region 텍스트 박스 키 DOWN 처리하기 - textBox_KeyDown(sender, e) /// <summary> /// 텍스트 박스 키 DOWN 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void textBox_KeyDown(object sender, KeyEventArgs e) { if(e.Key == Key.Enter) { DependencyObject scope = FocusManager.GetFocusScope(this.textBox); FocusManager.SetFocusedElement(scope, null); Keyboard.ClearFocus(); e.Handled = true; } } #endregion } } |
TestProject.zip
■ GetKeyboardLayoutName API 함수를 사용해 키보드 레이아웃 ID를 구하는 방법을 보여준다. ▶ Program.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
using System; using System.Runtime.InteropServices; using System.Text; namespace TestProject { /// <summary> /// 프로그램 /// </summary> class Program { //////////////////////////////////////////////////////////////////////////////////////////////////// Import ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region 키보드 레이아웃명 구하기 - GetKeyboardLayoutName(stringBuilder) /// <summary> /// 키보드 레이아웃명 구하기 /// </summary> /// <param name="stringBuilder">문자열 빌더</param> /// <returns>처리 결과</returns> [DllImport("user32")] private static extern int GetKeyboardLayoutName(StringBuilder stringBuilder); #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region 프로그램 시작하기 - Main() /// <summary> /// 프로그램 시작하기 /// </summary> private static void Main() { StringBuilder stringBuilder = new StringBuilder(10); GetKeyboardLayoutName(stringBuilder); Console.WriteLine($"키보드 레이아웃 ID : {stringBuilder.ToString()}"); } #endregion } } |
TestProject.zip
■ SendKeys 클래스의 SendWait 정적 메소드를 사용해 메모장에 문자열을 추가하는 방법을 보여준다. ▶ Program.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; namespace TestProject { /// <summary> /// 프로그램 /// </summary> class Program { //////////////////////////////////////////////////////////////////////////////////////////////////// Import ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region 전경 윈도우 설정하기 - SetForegroundWindow(windowHandle) /// <summary> /// 전경 윈도우 설정하기 /// </summary> /// <param name="windowHandle">윈도우 핸들</param> /// <returns>처리 결과</returns> [DllImport("user32", SetLastError = true)] private static extern bool SetForegroundWindow(IntPtr windowHandle); #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region 프로그램 시작하기 - Main() /// <summary> /// 프로그램 시작하기 /// </summary> private static void Main() { Process[] processArray = Process.GetProcessesByName("notepad"); foreach(Process process in processArray) { SetForegroundWindow(process.MainWindowHandle); SendKeys.SendWait("Hello, world!"); } } #endregion } } |
TestProject.zip
■ Console 클래스의 KeyAvailable 정적 속성을 사용하는 방법을 보여준다. ▶ Program.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
using System; using System.Runtime.InteropServices; namespace TestProject { /// <summary> /// 프로그램 /// </summary> class Program { //////////////////////////////////////////////////////////////////////////////////////////////////// Import ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region 커서 위치 구하기 - GetCursorPos(point) /// <summary> /// 커서 위치 구하기 /// </summary> /// <param name="point">포인트</param> /// <returns>처리 결과</returns> [DllImport("user32")] private static extern bool GetCursorPos(out POINT point); #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// X /// </summary> private static int _x; /// <summary> /// Y /// </summary> private static int _y; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region 마우스 위치 출력하기 - PrintMousePosition() /// <summary> /// 마우스 위치 출력하기 /// </summary> private static void PrintMousePosition() { POINT point; if(GetCursorPos(out point) && point.X != _x && point.Y != _y) { Console.Clear(); Console.WriteLine($"({point.X},{point.Y})"); _x = point.X; _y = point.Y; } } #endregion #region 프로그램 시작하기 - Main() /// <summary> /// 프로그램 시작하기 /// </summary> private static void Main() { Console.CursorVisible = false; while(!Console.KeyAvailable) { PrintMousePosition(); } Console.CursorVisible = true; } #endregion } } |
TestProject.zip
■ keybd_event API 함수를 사용해 SCROLL LOCK/NUM LOCK/CAPS LOCK 키를 토글하는 방법을 보여준다. ▶ Program.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
using System; using System.Runtime.InteropServices; namespace TestProject { /// <summary> /// 프로그램 /// </summary> class Program { //////////////////////////////////////////////////////////////////////////////////////////////////// Import ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region 키보드 이벤트 발생시키기 - keybd_event(virtualKey, scanCode, flag, extraInformationHandle) /// <summary> /// 키보드 이벤트 발생시키기 /// </summary> /// <param name="virtualKey">가상 키</param> /// <param name="scanCode">스캔 코드</param> /// <param name="flag">플래그</param> /// <param name="extraInformationHandle">부가 정보 핸들</param> [DllImport("user32")] private static extern void keybd_event(byte virtualKey, byte scanCode, uint flag, IntPtr extraInformationHandle); #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// KEYEVENTF_EXTENDEDKEY /// </summary> private const uint KEYEVENTF_EXTENDEDKEY = 0x01; /// <summary> /// KEYEVENTF_KEYUP /// </summary> private const uint KEYEVENTF_KEYUP = 0x02; /// <summary> /// VK_SCROLL /// </summary> private const byte VK_SCROLL = 0x91; /// <summary> /// VK_NUMLOCK /// </summary> private const byte VK_NUMLOCK = 0x90; /// <summary> /// VK_CAPITAL /// </summary> private const byte VK_CAPITAL = 0x14; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region 프로그램 시작하기 - Main() /// <summary> /// 프로그램 시작하기 /// </summary> private static void Main() { uint keyDown = KEYEVENTF_EXTENDEDKEY; uint keyUp = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP; keybd_event(VK_NUMLOCK, (byte)0, keyDown, new IntPtr(0)); keybd_event(VK_NUMLOCK, (byte)0, keyUp , new IntPtr(0)); keybd_event(VK_CAPITAL, (byte)0, keyDown, new IntPtr(0)); keybd_event(VK_CAPITAL, (byte)0, keyUp , new IntPtr(0)); keybd_event(VK_SCROLL, (byte)0, keyDown, new IntPtr(0)); keybd_event(VK_SCROLL, (byte)0, keyUp , new IntPtr(0)); } #endregion } } |
TestProject.zip
■ GetKeyState API 함수를 사용해 SCROLL LOCK/NUM LOCK/CAPS LOCK 키 눌림 여부를 구하는 방법을 보여준다. ▶ Program.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
using System; using System.Runtime.InteropServices; using System.Threading; namespace TestProject { /// <summary> /// 프로그램 /// </summary> class Program { //////////////////////////////////////////////////////////////////////////////////////////////////// Import ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region 키 상태 구하기 - GetKeyState(keyCode) /// <summary> /// 키 상태 구하기 /// </summary> /// <param name="keyCode">키 코드</param> /// <returns>키 상태</returns> [DllImport("user32")] private static extern short GetKeyState(int keyCode); #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// VK_CAPITAL /// </summary> private const int VK_CAPITAL = 0x14; /// <summary> /// VK_NUMLOCK /// </summary> private const int VK_NUMLOCK = 0x90; /// <summary> /// VK_SCROLL /// </summary> private const int VK_SCROLL = 0x91; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Private #region 프로그램 시작하기 - Main() /// <summary> /// 프로그램 시작하기 /// </summary> private static void Main() { while(true) { bool scrollLock = Convert.ToBoolean(GetKeyState(VK_SCROLL ) & 1); bool numLock = Convert.ToBoolean(GetKeyState(VK_NUMLOCK) & 1); bool capsLock = Convert.ToBoolean(GetKeyState(VK_CAPITAL) & 1); if(scrollLock) { Console.WriteLine("SCROLL LOCK ON"); } if(numLock) { Console.WriteLine("NUM LOCK ON"); } if(capsLock) { Console.WriteLine("CAPS LOCK ON"); } Thread.Sleep(500); } } #endregion } } |
TestProject.zip
■ Console 클래스의 CapsLock 정적 속성을 사용해 CAPS LOCK 키 눌림 여부를 구하는 방법을 보여준다. ▶ 예제 코드 (C#)
1 2 3 4 5 6 7 8 |
using System; if(Console.CapsLock) { Console.WriteLine("Caps Lock 키를 눌렀습니다."); } |