2018/12/17

[C#] WPF Keyboard Simulator

這篇用WPF 寫一個模擬Windows 鍵盤的APP。
其實關鍵就只是利用C# SendKeys 的功能而已(這篇有寫過)
但如果要寫一個完整模擬鍵盤的APP,會有些比較複雜的功能。
先準備好所有鍵盤的圖檔(圖檔來源),Layout 的部分這邊主要都會用程式碼來寫,要不然在xaml 要做一堆物件實在是有點麻煩。

一開始會打算先用字串陣列的方式,順序就依照鍵盤的設計去準備每個按鍵的圖案跟Keycode。
首先是鍵盤圖檔所有的檔名,包含大小寫轉換的圖案。
路徑的部分依照按下的狀態分為兩個資料夾來放(Keyup and Keydown),Keydown 是按下去時反白的圖案,圖檔格式為.png:
  1. private String FileType = ".png";
  2. private String FilePathKeyUp = "Image/KeyUp/keyup_";
  3. private String FilePathKeyDown = "Image/KeyDown/keydown_";
  4.  
  5. private String[] CapsLetterFileName = {"q","w","e","r","t","y","u","i","o","p","delete","one","two","three",
  6. "a","s","d","f","g","h","j","k","l","double_quote","enter","four","five","six",
  7. "shift","z","x","c","v","b","n","m","semicolon","colon","exclamation_mark_screamer","shift2","seven","eight","nine",
  8. "ctrl","alt","windows","spacebar","left","right","keyboard","zero","lperiod_full_stop"};
  9.  
  10. private String[] SmallLetterFileName = {"lq","lw","le","lr","lt","ly","lu","li","lo","lp","delete","one","two","three",
  11. "la","ls","ld","lf","lg","lh","lj","lk","ll","lsingle_quote","enter","four","five","six",
  12. "shift","lz","lx","lc","lv","lb","ln","lm","lcomma","lperiod_full_stop","lquestion_mark","shift2","seven","eight","nine",
  13. "ctrl","alt","windows","spacebar","left","right","keyboard","zero","lperiod_full_stop"};

再來是Keycode的部分,對應到上面的陣列位置,分別準備大、小寫的陣列。
  1. private String[] KeyCodeCapsLetter = {"Q","W","E","R","T","Y","U","I","O","P", "{backspace}","1","2","3",
  2. "A","S","D","F","G","H","J","K","L","\"","{enter}","4","5","6",
  3. "shift","Z","X","C","V","B","N","M",";",":","!","shift","7","8","9",
  4. "^","%","^" + "{esc}"," ","{left}","{right}","keyboard","0","."};
  5.  
  6. private String[] KeyCodeSmallLetter = {"q","w","e","r","t","y","u","i","o","p", "{backspace}","1","2","3",
  7. "a","s","d","f","g","h","j","k","l","'","{enter}","4","5","6",
  8. "shift","z","x","c","v","b","n","m",",",".","?","shift","7","8","9",
  9. "^","%","^" + "{esc}"," ","{left}","{right}","keyboard","0","."};

這樣之後程式碼寫輸入的部分,就可以用for 迴圈搭配if 判斷陣列位置這樣來寫:
  1. for (int i = 0; i < key_amount; i++)
  2. {
  3. if (mImage.Name == CapsLetterFileName[i])
  4. {
  5. if (isCaps)
  6. {
  7. SendKeys.SendWait(KeyCodeCapsLetter[i]);
  8. }
  9. else
  10. {
  11. SendKeys.SendWait(KeyCodeSmallLetter[i]);
  12. }
  13. }
  14. }

寫到這邊先來講一個問題,一般APP 出現在螢幕上的時候,視窗一定要Focus 才能用,但是要模擬鍵盤功能的話,是要針對APP 以外的其他視窗打字。
所以這邊的作法是先把APP 設定為Topmost (在xaml 中的Window 物件 > 屬性 > 一般 > Topmost)。
然後在cs 程式碼中宣告的地方import 需要的dll :
  1. // import dll to control on Topmost
  2. [DllImport("user32", SetLastError = true)]
  3. private extern static int GetWindowLong(IntPtr hwnd, int nIndex);
  4. [DllImport("user32", SetLastError = true)]
  5. private extern static int SetWindowLong(IntPtr hwnd, int nIndex, int dwNewValue);

在程式碼中建立一個函式 initTopmostControl() :
  1. private void initTopmostControl()
  2. {
  3. WindowInteropHelper wih = new WindowInteropHelper(this);
  4. int exstyle = GetWindowLong(wih.Handle, -20);
  5. exstyle |= 0x08000000;
  6. SetWindowLong(wih.Handle, -20, exstyle);
  7. }

這邊也讓Keyboard APP 模擬真實的鍵盤,可以在右下角看到APP 的圖示,甚至可以點它來開啟/隱藏Keyboard。
可以參考之前寫過的NotifyIcon 寫法(參考這篇):
建立一個函式 initNotifyIcon():
  1. // init NotifyIcon
  2. private void initNotifyIcon()
  3. {
  4. notifyIcon = new NotifyIcon();
  5. Stream iconStream = System.Windows.Application.GetResourceStream(new Uri(@"Image/icon2.ico", UriKind.Relative)).Stream;
  6. notifyIcon.Icon = new System.Drawing.Icon(iconStream);
  7. System.Windows.Forms.ContextMenu notifyIconMenu = new System.Windows.Forms.ContextMenu();
  8. System.Windows.Forms.MenuItem notifyIconMenuItem = new System.Windows.Forms.MenuItem();
  9. notifyIconMenuItem.Index = 0;
  10. notifyIconMenuItem.Text = "Exit";
  11. notifyIconMenuItem.Click += new EventHandler(notifyIconMenuItem_Click);
  12. notifyIconMenu.MenuItems.Add(notifyIconMenuItem);
  13. notifyIcon.ContextMenu = notifyIconMenu;
  14. notifyIcon.Visible = true;
  15. notifyIcon.Click += new EventHandler(TouckKeyboard_state);
  16. }

這邊NotifyIcon 想要加入的功能是,在icon 上點右鍵選擇Exit 來關閉APP。
notifyIconMenuItem.Click += new EventHandler(notifyIconMenuItem_Click);
其中notifyIconMenuItem_Click就是寫直接關閉APP
  1. private void notifyIconMenuItem_Click(object sender, EventArgs e)
  2. {
  3. this.Close();
  4. }

另外,可以在右下角的icon 上,點左鍵來顯示/隱藏APP,
notifyIcon.Click += new EventHandler(TouckKeyboard_state);
其中TouckKeyboard_state 就是來控制Keyboard APP 視窗顯示的位置,判斷isShow 然後利用Top 值來設定式窗位置,所以程式碼還要加入:
  1. // show or hide keyboard
  2. private void TouckKeyboard_state(object sender, EventArgs e)
  3. {
  4. isShow = !isShow;
  5. TouckKeyboard_position();
  6. }
  7.  
  8. // show or hide keyboard position
  9. private void TouckKeyboard_position()
  10. {
  11. if (!isShow)
  12. {
  13. while (this.Top < workArea.Height + 50)
  14. {
  15. this.Top++;
  16. }
  17. }
  18. else
  19. {
  20. while (this.Top >= workArea.Height - this.Height + 1)
  21. {
  22. this.Top--;
  23. }
  24. }
  25. }

workArea 的部分是建立一個Rect 來抓系統中,整個螢幕的範圍,可以透過它來設定視窗位置,或是做APP 等比例的縮放:
  1. private Rect workArea;
  2. workArea = System.Windows.SystemParameters.WorkArea;

以上三個函式initTopmostControl()、initNotifyIcon()、TouckKeyboard_position() 建議寫在Window_Loaded() 裡面比較保險。
在xaml 中的Window 物件 > 屬性 > 事件 > Loaded 點開產生函式:
  1. private void Window_Loaded(object sender, RoutedEventArgs e)
  2. {
  3. initTopmostControl();
  4.  
  5. initNotifyIcon();
  6.  
  7. TouckKeyboard_position();
  8. }


目前是寫針對APP 方面前提的一些基本的功能,接下來是比較重要的部分,就是每個按鍵的設定跟功能寫法。

在MainWindow() 先利用前面寫的workArea 來設定APP是窗的位置跟大小:
  1. public MainWindow()
  2. {
  3. InitializeComponent();
  4. // set keyboard window x,y,w,h by workArea
  5. workArea = System.Windows.SystemParameters.WorkArea;
  6. this.Left = 0;
  7. this.Top = workArea.Height;
  8. this.Width = workArea.Width;
  9. this.Height = workArea.Height * 0.435;
  10. }

這邊補上一個Layout 的參數來參考一下(參考圖檔來源的 spec.jpg):

建立一個函式initKeyImage() 來設定所有的按鍵圖案,包括圖檔、大小、位置:
  1. private void initKeyImage()
  2. {
  3. key_amount = CapsLetterFileName.Length;
  4. LetterFileName = new String[key_amount];
  5. imageLetter = new Image[key_amount];
  6. for (int i = 0; i < key_amount; i++)
  7. {
  8. // create key image
  9. imageLetter[i] = new Image();
  10. imageLetter[i].Name = CapsLetterFileName[i];
  11. imageLetter[i].Source = new BitmapImage(new Uri(@FilePathKeyUp + CapsLetterFileName[i] + FileType, UriKind.Relative));
  12. imageLetter[i].Stretch = Stretch.Fill;
  13. imageLetter[i].MouseDown += new MouseButtonEventHandler(imageLetterMouseDown);
  14. imageLetter[i].MouseUp += new MouseButtonEventHandler(imageLetterMouseUp);
  15. imageLetter[i].MouseLeave += new System.Windows.Input.MouseEventHandler(imageLetterMouseLeave);
  16. CanvasWindow.Children.Add(imageLetter[i]);
  17.  
  18. // set left of each key image
  19. if (imageLetter[i].Name == "q" || imageLetter[i].Name == "shift" || imageLetter[i].Name == "ctrl")
  20. {
  21. Canvas.SetLeft(imageLetter[i], workArea.Width * 0.04);
  22. }
  23. else if (imageLetter[i].Name == "a")
  24. {
  25. Canvas.SetLeft(imageLetter[i], workArea.Width * 0.053);
  26. }
  27. else
  28. {
  29. Canvas.SetLeft(imageLetter[i], Canvas.GetLeft(imageLetter[i - 1]) + imageLetter[i - 1].Width + 10);
  30. }
  31.  
  32. // set top of each key image
  33. if (i >= 0 && i <= 13)
  34. {
  35. Canvas.SetTop(imageLetter[i], workArea.Width * 0.01);
  36. }
  37. else if (i >= 14 && i <= 27)
  38. {
  39. Canvas.SetTop(imageLetter[i], (workArea.Width * 0.01 + (workArea.Height * 0.091 + workArea.Width * 0.004) * 1));
  40. }
  41. else if (i >= 28 && i <= 42)
  42. {
  43. Canvas.SetTop(imageLetter[i], (workArea.Width * 0.01 + (workArea.Height * 0.091 + workArea.Width * 0.004) * 2));
  44. }
  45. else
  46. {
  47. Canvas.SetTop(imageLetter[i], (workArea.Width * 0.01 + (workArea.Height * 0.091 + workArea.Width * 0.004) * 3));
  48. }
  49.  
  50. // set width of each key iamge
  51. if (imageLetter[i].Name == "delete" || imageLetter[i].Name == "zero")
  52. {
  53. imageLetter[i].Width = workArea.Width * 0.111;
  54. }
  55. else if (imageLetter[i].Name == "enter")
  56. {
  57. imageLetter[i].Width = workArea.Width * 0.098;
  58. }
  59. else if (imageLetter[i].Name == "spacebar")
  60. {
  61. imageLetter[i].Width = workArea.Width * 0.345;
  62. }
  63. else
  64. {
  65. imageLetter[i].Width = workArea.Width * 0.053;
  66. }
  67.  
  68. // set height of each key iamge
  69. imageLetter[i].Height = workArea.Height * 0.091;
  70. }
  71. }

建立滑鼠按下事件函式,圖案會呈現反白的樣子:
  1. private void imageLetterMouseDown(object sender, RoutedEventArgs e)
  2. {
  3. isDown = true;
  4. Image mImage = (Image) sender;
  5.  
  6. // set key pressed
  7. for (int i = 0; i < key_amount; i++)
  8. {
  9. if (mImage.Name == CapsLetterFileName[i])
  10. {
  11. key_pressed = i;
  12. }
  13. }
  14.  
  15. // set the keydown status and image
  16. setKeyStatus("down");
  17. // set long press
  18. if (mImage.Name != "shift" && mImage.Name != "shift2")
  19. {
  20. SetLongPressTimer(true);
  21. }
  22. }

建立滑鼠移開事件函式,做類似 mouse up的功能,但不要做任何事情:
  1. private void imageLetterMouseLeave(object sender, RoutedEventArgs e)
  2. {
  3. Image mImage = (Image)sender;
  4.  
  5. // set the keyup status and image
  6. setKeyStatus("up");
  7. SetLongPressTimer(false);
  8. }

建立滑鼠放開事件函式,在這裡就要寫判斷按了哪一個按鍵,做出對應的Keycode 功能:
  1. private void imageLetterMouseUp(object sender, RoutedEventArgs e)
  2. {
  3. Image mImage = (Image)sender;
  4.  
  5. // set the keyup status and image
  6. setKeyStatus("up");
  7.  
  8. // send key function
  9. if (isDown)
  10. {
  11. // shift function
  12. if (mImage.Name == "shift" || mImage.Name == "shift2")
  13. {
  14. isCaps = !isCaps;
  15. setLetterCaps(isCaps);
  16. }
  17.  
  18. // keyboard show function
  19. else if (mImage.Name == "keyboard")
  20. {
  21. isShow = !isShow;
  22. TouckKeyboard_position();
  23. }
  24. // letter function
  25. else
  26. {
  27. for (int i = 0; i < key_amount; i++)
  28. {
  29. if (mImage.Name == CapsLetterFileName[i])
  30. {
  31. if (isCaps)
  32. {
  33. SendKeys.SendWait(KeyCodeCapsLetter[i]);
  34. }
  35. else
  36. {
  37. SendKeys.SendWait(KeyCodeSmallLetter[i]);
  38. }
  39. }
  40. }
  41. }
  42. SetLongPressTimer(false);
  43. }
  44. }

在以上滑鼠事件中,down 跟up 事件裡有狀態切換的函式setKeyStatus(),跟計時器的函式SetLongPressTimer()。
在setKeyStatus() 裡面做的就是鍵盤沒按/按下的狀態切換,按下會反白,寫一個setKeyImage()函式來切換不同狀態的圖案。
  1. // set key status
  2. private void setKeyStatus(String status)
  3. {
  4. if(status == "down")
  5. {
  6. setKeyImage(imageLetter[key_pressed], FilePathKeyDown + LetterFileName[key_pressed] + FileType);
  7. }
  8. else if (status == "up")
  9. {
  10. setKeyImage(imageLetter[key_pressed], FilePathKeyUp + LetterFileName[key_pressed] + FileType);
  11. }
  12. }
  13.  
  14. // set key image source
  15. private void setKeyImage(Image image, String filepath)
  16. {
  17. image.Source = new BitmapImage(new Uri(@filepath, UriKind.Relative));
  18. }

另外就是函式SetLongPressTimer() 來控制長按用的計時器LongPressTimer 開啟/結束:
  1. private void SetLongPressTimer(bool isStart)
  2. {
  3. if (isStart)
  4. {
  5. LongPressTimer.Start();
  6. }
  7. else
  8. {
  9. LongPressTimer.Stop();
  10. pressed_count = 0;
  11. isDown = false;
  12. }
  13. }

當按下shift 時會切換大小寫模式,所以要建立一個函式setLetterCaps() 來切換大小寫模式:
  1. private void setLetterCaps(bool isCaps)
  2. {
  3. for (int i = 0; i < key_amount; i++)
  4. {
  5. if (isCaps)
  6. {
  7. LetterFileName[i] = CapsLetterFileName[i];
  8. }
  9. else
  10. {
  11. LetterFileName[i] = SmallLetterFileName[i];
  12. }
  13. setKeyImage(imageLetter[i], FilePathKeyUp + LetterFileName[i] + FileType);
  14. }
  15. }

MainWindow() 加入initKeyImage() 跟 setLetterCaps(true),另外也加入長按功能:
  1. private bool isCaps = true;
  2. private DispatcherTimer LongPressTimer;
  3.  
  4. public MainWindow()
  5. {
  6. InitializeComponent();
  7. // set keyboard window x,y,w,h by workArea
  8. workArea = System.Windows.SystemParameters.WorkArea;
  9. this.Left = 0;
  10. this.Top = workArea.Height;
  11. this.Width = workArea.Width;
  12. this.Height = workArea.Height * 0.435;
  13.  
  14. // set long press timer
  15. LongPressTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(0.01) };
  16. LongPressTimer.Tick += LongPressTimerTick;
  17.  
  18. initKeyImage();
  19. setLetterCaps(isCaps);
  20. }

在程式碼中加入計時器函式 LongPressTimerTick(),當長按時會一直輸入:
  1. private void LongPressTimerTick(object sender, EventArgs e)
  2. {
  3. pressed_count ++;
  4. if (pressed_count > 15)
  5. {
  6. if (isCaps)
  7. {
  8. SendKeys.SendWait(KeyCodeCapsLetter[key_pressed]);
  9. }
  10. else
  11. {
  12. SendKeys.SendWait(KeyCodeSmallLetter[key_pressed]);
  13. }
  14. }
  15. }

APP 執行的時候,可以從工具列的右下角看程式有沒有打開。
最後附上全部完整的程式碼:
MainWindow.cs:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using System.Windows.Data;
  9. using System.Windows.Documents;
  10. using System.Windows.Forms;
  11. using System.Windows.Interop;
  12. using System.Windows.Media;
  13. using System.Windows.Media.Imaging;
  14. using System.Windows.Navigation;
  15. using System.Windows.Shapes;
  16. using System.Runtime.InteropServices;
  17. using System.IO;
  18. using System.Windows.Threading;
  19. using System.Windows.Input;
  20.  
  21. namespace TouchKeyboard
  22. {
  23. public partial class MainWindow : Window
  24. {
  25. private bool isShow = true, isCaps = true, isDown = false;
  26.  
  27. private int key_amount, key_pressed, pressed_count = 0;
  28. private String FileType = ".png";
  29. private String FilePathKeyUp = "Image/KeyUp/keyup_";
  30. private String FilePathKeyDown = "Image/KeyDown/keydown_";
  31.  
  32. private String[] CapsLetterFileName = {"q","w","e","r","t","y","u","i","o","p","delete","one","two","three",
  33. "a","s","d","f","g","h","j","k","l","double_quote","enter","four","five","six",
  34. "shift","z","x","c","v","b","n","m","semicolon","colon","exclamation_mark_screamer","shift2","seven","eight","nine",
  35. "ctrl","alt","windows","spacebar","left","right","keyboard","zero","lperiod_full_stop"};
  36.  
  37. private String[] SmallLetterFileName = {"lq","lw","le","lr","lt","ly","lu","li","lo","lp","delete","one","two","three",
  38. "la","ls","ld","lf","lg","lh","lj","lk","ll","lsingle_quote","enter","four","five","six",
  39. "shift","lz","lx","lc","lv","lb","ln","lm","lcomma","lperiod_full_stop","lquestion_mark","shift2","seven","eight","nine",
  40. "ctrl","alt","windows","spacebar","left","right","keyboard","zero","lperiod_full_stop"};
  41.  
  42. private String[] KeyCodeCapsLetter = {"Q","W","E","R","T","Y","U","I","O","P", "{backspace}","1","2","3",
  43. "A","S","D","F","G","H","J","K","L","\"","{enter}","4","5","6",
  44. "shift","Z","X","C","V","B","N","M",";",":","!","shift","7","8","9",
  45. "^","%","^" + "{esc}"," ","{left}","{right}","keyboard","0","."};
  46.  
  47. private String[] KeyCodeSmallLetter = {"q","w","e","r","t","y","u","i","o","p", "{backspace}","1","2","3",
  48. "a","s","d","f","g","h","j","k","l","'","{enter}","4","5","6",
  49. "shift","z","x","c","v","b","n","m",",",".","?","shift","7","8","9",
  50. "^","%","^" + "{esc}"," ","{left}","{right}","keyboard","0","."};
  51.  
  52. private String[] LetterFileName;
  53. private Image[] imageLetter;
  54.  
  55. private Rect workArea;
  56. private NotifyIcon notifyIcon;
  57. private DispatcherTimer LongPressTimer;
  58.  
  59. // import dll to control on Topmost
  60. [DllImport("user32", SetLastError = true)]
  61. private extern static int GetWindowLong(IntPtr hwnd, int nIndex);
  62. [DllImport("user32", SetLastError = true)]
  63. private extern static int SetWindowLong(IntPtr hwnd, int nIndex, int dwNewValue);
  64. public MainWindow()
  65. {
  66. InitializeComponent();
  67. // set keyboard window x,y,w,h by workArea
  68. workArea = System.Windows.SystemParameters.WorkArea;
  69. this.Left = 0;
  70. this.Top = workArea.Height;
  71. this.Width = workArea.Width;
  72. this.Height = workArea.Height * 0.435;
  73.  
  74. // set long press timer
  75. LongPressTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(0.01) };
  76. LongPressTimer.Tick += LongPressTimerTick;
  77.  
  78. initKeyImage();
  79. setLetterCaps(isCaps);
  80. }
  81.  
  82. private void Window_Loaded(object sender, RoutedEventArgs e)
  83. {
  84. initTopmostControl();
  85.  
  86. initNotifyIcon();
  87.  
  88. TouckKeyboard_position();
  89. }
  90.  
  91. // control on Topmost
  92. private void initTopmostControl()
  93. {
  94. WindowInteropHelper wih = new WindowInteropHelper(this);
  95. int exstyle = GetWindowLong(wih.Handle, -20);
  96. exstyle |= 0x08000000;
  97. SetWindowLong(wih.Handle, -20, exstyle);
  98. }
  99.  
  100. // init NotifyIcon
  101. private void initNotifyIcon()
  102. {
  103. notifyIcon = new NotifyIcon();
  104. Stream iconStream = System.Windows.Application.GetResourceStream(new Uri(@"Image/icon2.ico", UriKind.Relative)).Stream;
  105. notifyIcon.Icon = new System.Drawing.Icon(iconStream);
  106. System.Windows.Forms.ContextMenu notifyIconMenu = new System.Windows.Forms.ContextMenu();
  107. System.Windows.Forms.MenuItem notifyIconMenuItem = new System.Windows.Forms.MenuItem();
  108. notifyIconMenuItem.Index = 0;
  109. notifyIconMenuItem.Text = "Exit";
  110. notifyIconMenuItem.Click += new EventHandler(notifyIconMenuItem_Click);
  111. notifyIconMenu.MenuItems.Add(notifyIconMenuItem);
  112. notifyIcon.ContextMenu = notifyIconMenu;
  113. notifyIcon.Visible = true;
  114. notifyIcon.Click += new EventHandler(TouckKeyboard_state);
  115. }
  116.  
  117. // init key image object
  118. private void initKeyImage()
  119. {
  120. key_amount = CapsLetterFileName.Length;
  121. LetterFileName = new String[key_amount];
  122. imageLetter = new Image[key_amount];
  123. for (int i = 0; i < key_amount; i++)
  124. {
  125. // create key image
  126. imageLetter[i] = new Image();
  127. imageLetter[i].Name = CapsLetterFileName[i];
  128. imageLetter[i].Source = new BitmapImage(new Uri(@FilePathKeyUp + CapsLetterFileName[i] + FileType, UriKind.Relative));
  129. imageLetter[i].Stretch = Stretch.Fill;
  130. imageLetter[i].MouseDown += new MouseButtonEventHandler(imageLetterMouseDown);
  131. imageLetter[i].MouseUp += new MouseButtonEventHandler(imageLetterMouseUp);
  132. imageLetter[i].MouseLeave += new System.Windows.Input.MouseEventHandler(imageLetterMouseLeave);
  133. CanvasWindow.Children.Add(imageLetter[i]);
  134.  
  135. // set left of each key image
  136. if (imageLetter[i].Name == "q" || imageLetter[i].Name == "shift" || imageLetter[i].Name == "ctrl")
  137. {
  138. Canvas.SetLeft(imageLetter[i], workArea.Width * 0.04);
  139. }
  140. else if (imageLetter[i].Name == "a")
  141. {
  142. Canvas.SetLeft(imageLetter[i], workArea.Width * 0.053);
  143. }
  144. else
  145. {
  146. Canvas.SetLeft(imageLetter[i], Canvas.GetLeft(imageLetter[i - 1]) + imageLetter[i - 1].Width + 10);
  147. }
  148.  
  149. // set top of each key image
  150. if (i >= 0 && i <= 13)
  151. {
  152. Canvas.SetTop(imageLetter[i], workArea.Width * 0.01);
  153. }
  154. else if (i >= 14 && i <= 27)
  155. {
  156. Canvas.SetTop(imageLetter[i], (workArea.Width * 0.01 + (workArea.Height * 0.091 + workArea.Width * 0.004) * 1));
  157. }
  158. else if (i >= 28 && i <= 42)
  159. {
  160. Canvas.SetTop(imageLetter[i], (workArea.Width * 0.01 + (workArea.Height * 0.091 + workArea.Width * 0.004) * 2));
  161. }
  162. else
  163. {
  164. Canvas.SetTop(imageLetter[i], (workArea.Width * 0.01 + (workArea.Height * 0.091 + workArea.Width * 0.004) * 3));
  165. }
  166.  
  167. // set width of each key iamge
  168. if (imageLetter[i].Name == "delete" || imageLetter[i].Name == "zero")
  169. {
  170. imageLetter[i].Width = workArea.Width * 0.111;
  171. }
  172. else if (imageLetter[i].Name == "enter")
  173. {
  174. imageLetter[i].Width = workArea.Width * 0.098;
  175. }
  176. else if (imageLetter[i].Name == "spacebar")
  177. {
  178. imageLetter[i].Width = workArea.Width * 0.345;
  179. }
  180. else
  181. {
  182. imageLetter[i].Width = workArea.Width * 0.053;
  183. }
  184.  
  185. // set height of each key iamge
  186. imageLetter[i].Height = workArea.Height * 0.091;
  187. }
  188. }
  189.  
  190. private void imageLetterMouseDown(object sender, RoutedEventArgs e)
  191. {
  192. isDown = true;
  193. Image mImage = (Image) sender;
  194.  
  195. // set key pressed
  196. for (int i = 0; i < key_amount; i++)
  197. {
  198. if (mImage.Name == CapsLetterFileName[i])
  199. {
  200. key_pressed = i;
  201. }
  202. }
  203.  
  204. // set the keydown status and image
  205. setKeyStatus("down");
  206. // set long press
  207. if (mImage.Name != "shift" && mImage.Name != "shift2")
  208. {
  209. SetLongPressTimer(true);
  210. }
  211. }
  212.  
  213. private void imageLetterMouseLeave(object sender, RoutedEventArgs e)
  214. {
  215. Image mImage = (Image)sender;
  216.  
  217. // set the keyup status and image
  218. setKeyStatus("up");
  219. SetLongPressTimer(false);
  220. }
  221.  
  222. private void imageLetterMouseUp(object sender, RoutedEventArgs e)
  223. {
  224. Image mImage = (Image)sender;
  225.  
  226. // set the keyup status and image
  227. setKeyStatus("up");
  228.  
  229. // send key function
  230. if (isDown)
  231. {
  232. // shift function
  233. if (mImage.Name == "shift" || mImage.Name == "shift2")
  234. {
  235. isCaps = !isCaps;
  236. setLetterCaps(isCaps);
  237. }
  238.  
  239. // keyboard show function
  240. else if (mImage.Name == "keyboard")
  241. {
  242. isShow = !isShow;
  243. TouckKeyboard_position();
  244. }
  245. // letter function
  246. else
  247. {
  248. for (int i = 0; i < key_amount; i++)
  249. {
  250. if (mImage.Name == CapsLetterFileName[i])
  251. {
  252. if (isCaps)
  253. {
  254. SendKeys.SendWait(KeyCodeCapsLetter[i]);
  255. }
  256. else
  257. {
  258. SendKeys.SendWait(KeyCodeSmallLetter[i]);
  259. }
  260. }
  261. }
  262. }
  263. SetLongPressTimer(false);
  264. }
  265. }
  266.  
  267. // set all letter caps or little
  268. private void setLetterCaps(bool isCaps)
  269. {
  270. for (int i = 0; i < key_amount; i++)
  271. {
  272. if (isCaps)
  273. {
  274. LetterFileName[i] = CapsLetterFileName[i];
  275. }
  276. else
  277. {
  278. LetterFileName[i] = SmallLetterFileName[i];
  279. }
  280. setKeyImage(imageLetter[i], FilePathKeyUp + LetterFileName[i] + FileType);
  281. }
  282. }
  283.  
  284. // set key status
  285. private void setKeyStatus(String status)
  286. {
  287. if(status == "down")
  288. {
  289. setKeyImage(imageLetter[key_pressed], FilePathKeyDown + LetterFileName[key_pressed] + FileType);
  290. }
  291. else if (status == "up")
  292. {
  293. setKeyImage(imageLetter[key_pressed], FilePathKeyUp + LetterFileName[key_pressed] + FileType);
  294. }
  295. }
  296.  
  297. // set key image source
  298. private void setKeyImage(Image image, String filepath)
  299. {
  300. image.Source = new BitmapImage(new Uri(@filepath, UriKind.Relative));
  301. }
  302.  
  303. // show or hide keyboard
  304. private void TouckKeyboard_state(object sender, EventArgs e)
  305. {
  306. isShow = !isShow;
  307. TouckKeyboard_position();
  308. }
  309.  
  310. // show or hide keyboard position
  311. private void TouckKeyboard_position()
  312. {
  313. if (!isShow)
  314. {
  315. while (this.Top < workArea.Height + 50)
  316. {
  317. this.Top++;
  318. }
  319. }
  320. else
  321. {
  322. while (this.Top >= workArea.Height - this.Height + 1)
  323. {
  324. this.Top--;
  325. }
  326. }
  327. }
  328. private void SetLongPressTimer(bool isStart)
  329. {
  330. if (isStart)
  331. {
  332. LongPressTimer.Start();
  333. }
  334. else
  335. {
  336. LongPressTimer.Stop();
  337. pressed_count = 0;
  338. isDown = false;
  339. }
  340. }
  341.  
  342. private void LongPressTimerTick(object sender, EventArgs e)
  343. {
  344. pressed_count ++;
  345. if (pressed_count > 15)
  346. {
  347. if (isCaps)
  348. {
  349. SendKeys.SendWait(KeyCodeCapsLetter[key_pressed]);
  350. }
  351. else
  352. {
  353. SendKeys.SendWait(KeyCodeSmallLetter[key_pressed]);
  354. }
  355. }
  356. }
  357. private void notifyIconMenuItem_Click(object sender, EventArgs e)
  358. {
  359. this.Close();
  360. }
  361.  
  362. private void Window_Closed(object sender, EventArgs e)
  363. {
  364. notifyIcon.Dispose();
  365. }
  366. }
  367. }

xaml:
  1. <Window x:Class="TouchKeyboard.MainWindow"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6. xmlns:local="clr-namespace:TouchKeyboard"
  7. mc:Ignorable="d"
  8. Title="MainWindow" Topmost="True" Loaded="Window_Loaded" Left="0" Top="0" Height="296" Width="1280" WindowStyle="None" Visibility="Visible" AllowsTransparency="True" Closed="Window_Closed" ResizeMode="NoResize" Focusable="False" ShowInTaskbar="False" Background="Black">
  9. <Grid>
  10. <Canvas x:Name="CanvasWindow">
  11. </Canvas>
  12. </Grid>
  13. </Window>


END

沒有留言:

張貼留言