using System; using System.IO; using System.Drawing; using System.Linq; using System.Windows.Forms; using IniParser; using IniParser.Model; namespace DualScreenDemo { public partial class PrimaryForm { // 拼音歌曲的 PictureBox private PictureBox pictureBoxPinYinSongs; // 存放拼音按鈕的陣列 private Button[] letterButtonsForPinYinSongs; // 特殊功能按鈕(修改、清除、關閉) private Button modifyButtonPinYinSongs; private Button clearButtonPinYinSongs; private Button closeButtonPinYinSongs; // 用於顯示輸入文字的輸入框 private RichTextBox inputBoxPinYinSongs; /// /// 拼音歌曲搜尋按鈕點擊事件 /// private void PinyinSearchSongsButton_Click(object sender, EventArgs e) { // 更新搜尋模式按鈕的背景圖 zhuyinSearchSongButton.BackgroundImage = zhuyinSearchSongNormalBackground; englishSearchSongButton.BackgroundImage = englishSearchSongNormalBackground; pinyinSearchSongButton.BackgroundImage = pinyinSearchSongActiveBackground; wordCountSearchSongButton.BackgroundImage = wordCountSearchSongNormalBackground; handWritingSearchSongButton.BackgroundImage = handWritingSearchSongNormalBackground; numberSearchSongButton.BackgroundImage = numberSearchSongNormalBackground; // 讀取 config.ini 並獲取拼音圖片的路徑 var configData = LoadConfigData(); string pinyinImagePath = Path.Combine(Application.StartupPath, configData["ImagePaths"]["PinYinSongs"]); // 顯示拼音歌曲圖片 ShowImageOnPictureBoxPinYinSongs(Path.Combine(Application.StartupPath, pinyinImagePath)); // 鍵盤UI介面顯示設定 SetWordCountSongsAndButtonsVisibility(false); SetEnglishSongsAndButtonsVisibility(false); SetPinYinSongsAndButtonsVisibility(true); SetHandWritingForSongsAndButtonsVisibility(false); SetSongIDSearchAndButtonsVisibility(false); SetZhuYinSongsAndButtonsVisibility(false); ResetinputBox(); pictureBoxPinYinSongs.Visible = true; } /// /// 初始化拼音按鈕 /// private void InitializeLetterButtonsForPinYinSongs() { // 從設定檔 (config.ini) 讀取配置數據 var data = LoadConfigData(); // 從配置數據中載入拼音字母按鈕的影像 (包含正常、點擊、滑鼠懸停三種狀態) var buttonImages = LoadButtonImages(data, "PinYinLetterButtonImages", 26); // 定義 QWERTY 鍵盤排列的字母順序 string qwertyLayout = "QWERTYUIOPASDFGHJKLZXCVBNM"; // 初始化拼音按鈕陣列,長度為 26(對應英文字母) letterButtonsForPinYinSongs = new Button[26]; // 迴圈遍歷 26 個字母,依序建立按鈕 for (int i = 0; i < 26; i++) { // 從配置檔案讀取當前按鈕的座標資訊 (X, Y, Width, Height) var coords = data["PinYinLetterButtonCoordinates"][$"button{i}"].Split(','); // 建立拼音按鈕,並設定名稱、座標、影像與事件處理函式 letterButtonsForPinYinSongs[i] = CreateButton( $"letterButton_{qwertyLayout[i]}", // 按鈕名稱,例如 "letterButton_Q" (int.Parse(coords[0]), int.Parse(coords[1]), int.Parse(coords[2]), int.Parse(coords[3])), // 解析座標數據 buttonImages[$"button{i}"].normal, // 正常狀態影像 buttonImages[$"button{i}"].mouseDown, // 按下狀態影像 buttonImages[$"button{i}"].mouseOver, // 滑鼠懸停狀態影像 LetterButtonPinYinSongs_Click // 點擊事件處理函式 ); // 設定按鈕的標籤 (Tag) 為對應的字母,例如 Q、W、E... letterButtonsForPinYinSongs[i].Tag = qwertyLayout[i]; // 將按鈕新增到表單的控制項集合中,讓其顯示在介面上 this.Controls.Add(letterButtonsForPinYinSongs[i]); } } /// /// 處理拼音按鈕點擊事件 /// /// 觸發事件按鈕 /// 事件參數 private void LetterButtonPinYinSongs_Click(object sender, EventArgs e) { // 嘗試將觸發事件的物件轉換為 Button 類型 var button = sender as Button; // 檢查按鈕是否為 null,並確保該按鈕的 Tag 屬性不為 null if (button != null && button.Tag != null) { // 確保輸入框 (inputBoxPinYinSongs) 是可見狀態 if (inputBoxPinYinSongs.Visible) { // 將按鈕的標籤 (Tag) 轉換為字串,並附加到輸入框的文字內容中 inputBoxPinYinSongs.Text += button.Tag.ToString(); } } } /// /// 初始化拼音輸入相關的 UI 控件,包括字母按鈕、特殊功能按鈕(修改、清除、關閉),以及拼音輸入框。 /// private void InitializeButtonsForPinYinSongs() { // 初始化拼音字母按鈕,根據 QWERTY 鍵盤佈局建立對應的按鈕 InitializeLetterButtonsForPinYinSongs(); // 初始化特殊功能按鈕(修改、清除、關閉) InitializeSpecialButtonsForPinYinSongs(); // 初始化拼音輸入框,使用者可透過輸入拼音來搜尋歌曲 InitializeInputBoxPinYinSongs(); } /// /// 初始化拼音輸入的特殊功能按鈕,包括: /// 1. 修改按鈕 - 刪除輸入框中的最後一個字母 /// 2. 清除按鈕 - 清空輸入框的內容 /// 3. 關閉按鈕 - 隱藏拼音輸入的 UI 元件 /// private void InitializeSpecialButtonsForPinYinSongs() { // 初始化「修改」按鈕(刪除輸入框最後一個字母) InitializeModifyButtonPinYinSongs(); // 初始化「清除」按鈕(清空輸入框內容) InitializeClearButtonPinYinSongs(); // 初始化「關閉」按鈕(關閉拼音輸入 UI) InitializeCloseButtonPinYinSongs(); } /// /// 初始化「修改」按鈕,提供刪除拼音輸入框最後一個字母的功能。 /// private void InitializeModifyButtonPinYinSongs() { // 讀取設定檔,載入特殊按鈕的配置資料 var data = LoadConfigData(); // 從設定檔取得「修改按鈕」的座標與大小 modifyButtonPinYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "modifyButtonPinYinSongs"); // 讀取「修改按鈕」的圖片資源(一般狀態、滑鼠懸停、按下) var buttonImages = LoadButtonImages(data, "ModifyButtonImagesPinYin"); // 創建「修改」按鈕,並綁定點擊事件 modifyButtonPinYinSongs = CreateSpecialButton( "btnModifyPinYinSongs", // 按鈕名稱 modifyButtonPinYinCoords, // 設定按鈕的座標與大小 buttonImages.normal, // 設定按鈕的正常狀態圖片 buttonImages.mouseOver, // 設定按鈕的滑鼠懸停圖片 buttonImages.mouseDown, // 設定按鈕的按下狀態圖片 ModifyButtonPinYinSongs_Click // 綁定按鈕的點擊事件處理函式 ); } /// /// 「修改」按鈕點擊事件:刪除拼音輸入框 (inputBoxPinYinSongs) 中的最後一個字母。 /// private void ModifyButtonPinYinSongs_Click(object sender, EventArgs e) { // 確保 inputBoxPinYinSongs 存在於視窗控制項集合內,且輸入框內有文字 if (this.Controls.Contains(inputBoxPinYinSongs) && inputBoxPinYinSongs.Text.Length > 0) { // 刪除輸入框內的最後一個字母 inputBoxPinYinSongs.Text = inputBoxPinYinSongs.Text.Substring(0, inputBoxPinYinSongs.Text.Length - 1); } } /// /// 初始化「清除」按鈕 (clearButtonPinYinSongs),用於清空拼音輸入框 (inputBoxPinYinSongs)。 /// private void InitializeClearButtonPinYinSongs() { // 從設定檔載入資料 var data = LoadConfigData(); // 讀取「清除」按鈕的座標配置 (X, Y, Width, Height) clearButtonPinYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "clearButtonPinYinSongs"); // 載入「清除」按鈕的圖片 (一般狀態、滑鼠懸停、按下時的圖片) var buttonImages = LoadButtonImages(data, "ClearButtonImagesPinYin"); // 建立「清除」按鈕,設定對應的座標與圖片,並綁定點擊事件 clearButtonPinYinSongs = CreateSpecialButton( "btnClearPinYinSongs", // 按鈕名稱 clearButtonPinYinCoords, // 設定按鈕的座標與大小 buttonImages.normal, // 設定按鈕的正常狀態圖片 buttonImages.mouseOver, // 設定按鈕的滑鼠懸停圖片 buttonImages.mouseDown, // 設定按鈕的按下狀態圖片 ClearButtonPinYinSongs_Click // 綁定按鈕的點擊事件處理函式 ); } /// /// 清空拼音輸入框的內容。 /// 當使用者點擊清除按鈕時,若輸入框存在且有內容,則將其清空。 /// private void ClearButtonPinYinSongs_Click(object sender, EventArgs e) { // 檢查視窗內是否包含 inputBoxPinYinSongs 控制項,且輸入框內是否有文字 if (this.Controls.Contains(inputBoxPinYinSongs) && inputBoxPinYinSongs.Text.Length > 0) { // 清空拼音輸入框的內容 inputBoxPinYinSongs.Text = ""; } } /// /// 初始化「關閉」按鈕 (closeButtonPinYinSongs),用於隱藏拼音輸入介面。 /// private void InitializeCloseButtonPinYinSongs() { // 讀取設定檔中的按鈕配置數據 var data = LoadConfigData(); // 從設定檔中取得「關閉」按鈕的座標 (X, Y, Width, Height) closeButtonPinYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "closeButtonPinYinSongs"); // 從設定檔中讀取「關閉」按鈕的圖片 (一般狀態、滑鼠懸停、按下時的圖片) var buttonImages = LoadButtonImages(data, "CloseButtonImagesPinYin"); // 建立「關閉」按鈕,設定名稱、座標、圖片及點擊事件 closeButtonPinYinSongs = CreateSpecialButton( "btnClosePinYinSongs", // 按鈕名稱 closeButtonPinYinCoords, // 設定按鈕的座標與大小 buttonImages.normal, // 設定按鈕的正常狀態圖片 buttonImages.mouseOver, // 設定按鈕的滑鼠懸停圖片 buttonImages.mouseDown, // 設定按鈕的按下狀態圖片 CloseButtonPinYinSongs_Click // 綁定按鈕的點擊事件處理函式 ); } /// /// 關閉拼音輸入模式,隱藏相關 UI 元件。 /// /// 觸發事件的按鈕。 /// 事件參數。 private void CloseButtonPinYinSongs_Click(object sender, EventArgs e) { // 隱藏拼音輸入的背景圖片 (可能是 UI 中的輸入框背景) pictureBoxPinYinSongs.Visible = false; pinyinSearchSongButton.BackgroundImage = pinyinSearchSongNormalBackground; // 設定拼音輸入框與所有相關按鈕的可見性為 false SetPinYinSongsAndButtonsVisibility(false); } /// /// 初始化拼音輸入框 (RichTextBox),並從 config.ini 讀取相關設定。 /// private void InitializeInputBoxPinYinSongs() { try { // 創建一個 INI 檔案解析器 var parser = new FileIniDataParser(); // 配置解析器的參數 parser.Parser.Configuration.AssigmentSpacer = ""; // 設定 = 兩側沒有空格 parser.Parser.Configuration.CommentString = "#"; // 使用 # 作為註解符號 parser.Parser.Configuration.CaseInsensitive = true; // 參數名稱不區分大小寫 IniData data; // 儲存解析後的 INI 數據 // 讀取 config.ini 文件,使用 UTF-8 編碼 using (var reader = new StreamReader("config.ini", System.Text.Encoding.UTF8)) { data = parser.ReadData(reader); } // **從 INI 檔案讀取拼音輸入框的位置與大小** int x = int.Parse(data["InputBoxPinYinSongs"]["X"]); // X 座標 int y = int.Parse(data["InputBoxPinYinSongs"]["Y"]); // Y 座標 int width = int.Parse(data["InputBoxPinYinSongs"]["Width"]); // 寬度 int height = int.Parse(data["InputBoxPinYinSongs"]["Height"]); // 高度 // **讀取字型設定** string fontName = data["InputBoxPinYinSongs"]["FontName"]; // 字型名稱 float fontSize = float.Parse(data["InputBoxPinYinSongs"]["FontSize"]); // 字體大小 FontStyle fontStyle = (FontStyle)Enum.Parse(typeof(FontStyle), data["InputBoxPinYinSongs"]["FontStyle"]); // 字體樣式 Color foreColor = Color.FromName(data["InputBoxPinYinSongs"]["ForeColor"]); // 文字顏色 // 創建拼音輸入框 (RichTextBox) inputBoxPinYinSongs = new RichTextBox { Visible = false, // 預設為隱藏 Name = "inputBoxPinYinSongs", // 設定控制項名稱 ForeColor = foreColor, // 設定文字顏色 Font = new Font( fontName, fontSize / 900 * Screen.PrimaryScreen.Bounds.Height, // 根據螢幕大小調整字體 fontStyle ) }; // 設定輸入框的位置與大小 ResizeAndPositionControl(inputBoxPinYinSongs, x, y, width, height); // **綁定 TextChanged 事件 (當輸入內容改變時觸發搜尋)** inputBoxPinYinSongs.TextChanged += (sender, e) => { string searchText = inputBoxPinYinSongs.Text; // 根據拼音前綴篩選歌曲 var searchResults = allSongs.Where(song => song.PinyinNotation.StartsWith(searchText)).ToList(); currentPage = 0; // 重置當前頁面索引 currentSongList = searchResults; // 更新搜尋結果 totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage); // 計算總頁數 // 更新 UI,顯示搜尋結果 multiPagePanel.currentPageIndex = 0; multiPagePanel.LoadSongs(currentSongList); }; // 將拼音輸入框加入視窗中 this.Controls.Add(inputBoxPinYinSongs); } catch (Exception ex) { // 發生錯誤時輸出錯誤訊息 (避免程式崩潰) Console.WriteLine($"An error occurred: {ex.Message}"); } } /// /// 存儲 PictureBoxPinYinSongs 的座標與尺寸信息。 /// /// /// 此元組包含以下四個值: /// X:X 座標 /// , Y:Y 座標 /// , Width:寬度 /// , Height:高度 /// private (int X, int Y, int Width, int Height) pictureBoxPinYinSongCoords; /// /// 從 config.ini 配置檔案中載入 PictureBoxPinYinSongs 的座標與尺寸設定。 /// private void LoadPictureBoxPinYinSongCoordsFromConfig() { // 創建一個 INI 檔案解析器 var parser = new FileIniDataParser(); // 讀取 config.ini 文件並解析成 IniData 對象 IniData data = parser.ReadFile("config.ini"); // 取得 PictureBoxPinYinSongs 區段的設定值 var coords = data["PictureBoxPinYinSongs"]; // 解析 X, Y, Width, Height,並存入 pictureBoxPinYinSongCoords pictureBoxPinYinSongCoords = ( int.Parse(coords["X"]), // 解析 X 座標 int.Parse(coords["Y"]), // 解析 Y 座標 int.Parse(coords["Width"]), // 解析 寬度 int.Parse(coords["Height"]) // 解析 高度 ); } /// /// 顯示拼音歌曲圖片 /// /// 圖片路徑 private void ShowImageOnPictureBoxPinYinSongs(string imagePath) { // 從設定檔載入 PictureBox 的座標與大小 LoadPictureBoxPinYinSongCoordsFromConfig(); // 使用指定的圖片路徑建立 Bitmap 影像 Bitmap originalImage = new Bitmap(imagePath); // 建立一個矩形,表示 PictureBox 應該顯示的範圍 Rectangle displayArea = new Rectangle( pictureBoxPinYinSongCoords.X, // 設定 X 座標 pictureBoxPinYinSongCoords.Y, // 設定 Y 座標 pictureBoxPinYinSongCoords.Width, // 設定 寬度 pictureBoxPinYinSongCoords.Height // 設定 高度 ); // 將載入的圖片設定為 pictureBoxPinYinSongs 的影像 pictureBoxPinYinSongs.Image = originalImage; // 調整 PictureBox 的大小與位置,使其符合 displayArea 的設定 ResizeAndPositionPictureBox( pictureBoxPinYinSongs, displayArea.X, displayArea.Y, displayArea.Width, displayArea.Height ); // 顯示 PictureBox pictureBoxPinYinSongs.Visible = true; } /// /// 設定拼音模式的 UI 是否可見 /// /// 是否可見 private void SetPinYinSongsAndButtonsVisibility(bool isVisible) { // 定義一個委派 (Action),用於更新 UI 控件的可見性 System.Action action = () => { // 暫停佈局更新,以防止 UI 閃爍或重繪時出現異常 SuspendLayout(); // 設定 pictureBoxPinYinSongs 的可見性 pictureBoxPinYinSongs.Visible = isVisible; if (isVisible) pictureBoxPinYinSongs.BringToFront(); // 確保顯示時位於最前方 // 設定所有拼音字母按鈕的可見性 foreach (var button in letterButtonsForPinYinSongs) { button.Visible = isVisible; if (isVisible) button.BringToFront(); } // 設定 modifyButtonPinYinSongs (修改按鈕) 的可見性 if (modifyButtonPinYinSongs != null) { modifyButtonPinYinSongs.Visible = isVisible; if (isVisible) modifyButtonPinYinSongs.BringToFront(); } // 設定 clearButtonPinYinSongs (清除按鈕) 的可見性 if (clearButtonPinYinSongs != null) { clearButtonPinYinSongs.Visible = isVisible; if (isVisible) clearButtonPinYinSongs.BringToFront(); } // 設定 closeButtonPinYinSongs (關閉按鈕) 的可見性 closeButtonPinYinSongs.Visible = isVisible; if (isVisible) closeButtonPinYinSongs.BringToFront(); // 設定 inputBoxPinYinSongs (輸入框) 的可見性 inputBoxPinYinSongs.Visible = isVisible; if (isVisible) inputBoxPinYinSongs.BringToFront(); // 恢復佈局,允許 UI 更新 ResumeLayout(); PerformLayout(); // 刷新 pictureBoxPinYinSongs,確保畫面更新 pictureBoxPinYinSongs.Refresh(); // 刷新拼音字母按鈕 foreach (var button in letterButtonsForPinYinSongs) { button.Refresh(); } // 刷新其他按鈕與輸入框 modifyButtonPinYinSongs.Refresh(); clearButtonPinYinSongs.Refresh(); closeButtonPinYinSongs.Refresh(); inputBoxPinYinSongs.Refresh(); }; // 如果當前執行緒不是 UI 執行緒,則使用 Invoke 確保執行於 UI 執行緒 if (this.InvokeRequired) { this.Invoke(action); } else { action(); } } } }