using System; using System.IO; using System.Drawing; using System.Drawing.Imaging; using System.Linq; using System.Windows.Forms; using System.Collections.Generic; using IniParser; using IniParser.Model; using System.Text; /* Song Search with ZhuYin */ namespace DualScreenDemo { public partial class PrimaryForm { // 注音歌曲的 PictureBox private PictureBox pictureBoxZhuYinSongs; //存放注音按鈕的陣列 private Button[] phoneticButtonsForSongs; //特殊功能按鈕(修改、清除、關閉) private Button modifyButtonZhuYinSongs; private Button clearButtonZhuYinSongs; private Button closeButtonZhuYinSongs; //用於顯示輸入文字的輸入框 private RichTextBox inputBoxZhuYinSongs; /// /// 注音歌曲搜尋按鈕點擊事件 /// private void ZhuyinSearchSongsButton_Click(object sender, EventArgs e) { //更新搜尋模式按鈕的背景圖 zhuyinSearchSongButton.BackgroundImage = zhuyinSearchSongActiveBackground; englishSearchSongButton.BackgroundImage = englishSearchSongNormalBackground; pinyinSearchSongButton.BackgroundImage = pinyinSearchSongNormalBackground; wordCountSearchSongButton.BackgroundImage = wordCountSearchSongNormalBackground; handWritingSearchSongButton.BackgroundImage = handWritingSearchSongNormalBackground; numberSearchSongButton.BackgroundImage = numberSearchSongNormalBackground; // 讀取 config.ini 並獲取注音圖片的路徑 var configData = LoadConfigData(); string imagePath = Path.Combine(Application.StartupPath, configData["ImagePaths"]["ZhuYinSongs"]); //顯示注音歌曲圖片 ShowImageOnPictureBoxZhuYinSongs(Path.Combine(Application.StartupPath, imagePath)); //設定不同模式的UI顯示 SetWordCountSongsAndButtonsVisibility(false); // 隱藏字數搜尋相關控件 SetEnglishSongsAndButtonsVisibility(false); SetPinYinSongsAndButtonsVisibility(false); SetHandWritingForSongsAndButtonsVisibility(false); SetSongIDSearchAndButtonsVisibility(false); SetZhuYinSongsAndButtonsVisibility(true); ResetinputBox(); pictureBoxZhuYinSongs.Visible = true; } /// /// 初始化注音按鈕 (Phonetic Buttons) 並載入其對應的圖片與座標 /// 1. 讀取 config.ini 設定檔,獲取按鈕的相關數據 (符號、座標、圖片) /// 2. 解析注音符號並儲存至 phoneticSymbols 陣列 /// 3. 解析按鈕的座標資訊,存入 phoneticButtonCoords /// 4. 解析按鈕的圖片 (正常狀態、按下狀態、懸停狀態),存入 phoneticButtonImages。 /// 5. 依序建立 35 個注音按鈕,並套用對應的圖片與事件處理函數。 /// private void InitializePhoneticButtonsForSongs() { // 1. 從設定檔 (config.ini) 讀取配置數據,包含按鈕座標、圖片等 var data = LoadConfigData(); // 2. 讀取注音符號列表,這些符號將用於顯示在按鈕上 phoneticSymbols = LoadPhoneticSymbols(data); // 3. 從設定檔載入 **注音按鈕的座標**,每個按鈕都有對應的 (X, Y, Width, Height) phoneticButtonCoords = LoadButtonCoordinates(data, "PhoneticButtonCoordinates", 35); // 4. 從設定檔載入 **注音按鈕的圖片**,每個按鈕都有正常、按下、懸停三種狀態 phoneticButtonImages = LoadButtonImages(data, "PhoneticButtonImages", 35); // 5. 建立 35 個注音按鈕的陣列 (每個按鈕對應一個注音符號) phoneticButtonsForSongs = new Button[35]; // 6. 迴圈建立所有的注音按鈕 for (int i = 0; i < 35; i++) { // 取得當前按鈕的圖片 (從已載入的 phoneticButtonImages 字典中獲取) var buttonImages = phoneticButtonImages[$"button{i}"]; // 建立單個注音按鈕,並設定其圖片與點擊事件 CreatePhoneticButtonForSongs( i, // 按鈕索引 (對應於 phoneticSymbols) buttonImages.normal, // 按鈕的普通狀態圖片 buttonImages.mouseDown, // 按下時的圖片 buttonImages.mouseOver // 滑鼠懸停時的圖片 ); } } /// /// 建立單個注音按鈕,並設定其圖片、大小、位置,以及滑鼠事件。 /// /// 按鈕索引 (對應於 phoneticSymbols) /// 按鈕的普通狀態圖片路徑 /// 按鈕被按下時的圖片路徑 /// 滑鼠懸停時的圖片路徑 private void CreatePhoneticButtonForSongs(int index, string normalImagePath, string mouseDownImagePath, string mouseOverImagePath) { try { // 1. 創建按鈕並初始化屬性 phoneticButtonsForSongs[index] = new Button { Name = $"phoneticButton_{phoneticSymbols[index]}", // 設定按鈕名稱,方便識別 BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath)), // 設定預設背景圖 BackgroundImageLayout = ImageLayout.Stretch, // 背景圖自動填滿按鈕 FlatStyle = FlatStyle.Flat, // 設定按鈕為扁平樣式 FlatAppearance = { BorderSize = 0 } // 移除按鈕的邊框 }; // 2. 設定按鈕的大小與位置 ResizeAndPositionButton( phoneticButtonsForSongs[index], phoneticButtonCoords[index].X, phoneticButtonCoords[index].Y, phoneticButtonCoords[index].Width, phoneticButtonCoords[index].Height ); // 3. 載入三種狀態的圖片 Image normalImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath)); Image mouseDownImage = Image.FromFile(Path.Combine(Application.StartupPath, mouseDownImagePath)); Image mouseOverImage = Image.FromFile(Path.Combine(Application.StartupPath, mouseOverImagePath)); // 4. 設定滑鼠事件,改變背景圖 phoneticButtonsForSongs[index].MouseDown += (s, e) => phoneticButtonsForSongs[index].BackgroundImage = mouseDownImage; phoneticButtonsForSongs[index].MouseUp += (s, e) => phoneticButtonsForSongs[index].BackgroundImage = normalImage; phoneticButtonsForSongs[index].MouseEnter += (s, e) => phoneticButtonsForSongs[index].BackgroundImage = mouseOverImage; phoneticButtonsForSongs[index].MouseLeave += (s, e) => phoneticButtonsForSongs[index].BackgroundImage = normalImage; // 5. 設定按鈕的點擊事件 (當按下按鈕時,觸發 PhoneticButton_Click) phoneticButtonsForSongs[index].Click += PhoneticButton_Click; // 6. 存入對應的注音符號,方便之後的處理 phoneticButtonsForSongs[index].Tag = phoneticSymbols[index]; // 7. 將按鈕加入視窗 this.Controls.Add(phoneticButtonsForSongs[index]); } catch (Exception ex) { // 例外處理,確保按鈕建立失敗時不影響其他部分 Console.WriteLine($"Error creating button at index {index}: {ex.Message}"); } } /// /// 初始化注音輸入界面的所有按鈕和輸入框。 /// private void InitializeButtonsForZhuYinSongs() { // 1. 從設定檔 (config.ini) 載入注音符號 LoadPhoneticSymbolsFromConfig(); // 2. 初始化 35 個注音按鈕 InitializePhoneticButtonsForSongs(); // 3. 初始化特殊按鈕 (例如:刪除、確定、返回按鈕) InitializeSpecialButtonsForZhuYinSongs(); // 4. 初始化輸入框 (用於顯示使用者輸入的注音符號) InitializeInputBoxZhuYinSongs(); } /// /// 初始化注音輸入界面的特殊按鈕,包括「修改」、「清除」和「關閉」。 /// private void InitializeSpecialButtonsForZhuYinSongs() { // 1. 初始化「修改」按鈕 (刪除上一個輸入的注音符號) InitializeModifyButtonZhuYinSongs(); // 2. 初始化「清除」按鈕 (清空所有輸入內容) InitializeClearButtonZhuYinSongs(); // 3. 初始化「關閉」按鈕 (關閉注音輸入介面) InitializeCloseButtonZhuYinSongs(); } /// /// 初始化「修改」按鈕,讓使用者可以刪除上一個輸入的注音符號。 /// private void InitializeModifyButtonZhuYinSongs() { // 1. 讀取設定檔 (config.ini) 來獲取特殊按鈕的座標與圖像資訊 var data = LoadConfigData(); // 2. 從設定檔讀取「修改」按鈕的座標數據 modifyButtonZhuYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "modifyButtonZhuYinSongs"); // 3. 從設定檔讀取「修改」按鈕的圖像 (Normal、MouseOver、MouseDown) var buttonImages = LoadButtonImages(data, "ModifyButtonImagesZhuYin"); // 4. 使用座標與圖像來建立「修改」按鈕,並綁定點擊事件 modifyButtonZhuYinSongs = CreateSpecialButton( "btnModifyZhuYinSongs", // 按鈕名稱 modifyButtonZhuYinCoords, // 按鈕座標 buttonImages.normal, // 預設 (normal) 圖像 buttonImages.mouseOver, // 滑鼠移過 (hover) 圖像 buttonImages.mouseDown, // 按下 (pressed) 圖像 ModifyButtonZhuYinSongs_Click // 綁定按鈕點擊事件 ); } /// /// 「修改」按鈕的點擊事件,刪除輸入框內的最後一個注音符號。 /// private void ModifyButtonZhuYinSongs_Click(object sender, EventArgs e) { // 1. 確保輸入框 (inputBoxZhuYinSongs) 存在於目前的視窗控制項中 if (this.Controls.Contains(inputBoxZhuYinSongs) && inputBoxZhuYinSongs.Text.Length > 0) { // 2. 刪除輸入框中的最後一個字元 (即移除最後一個注音符號) inputBoxZhuYinSongs.Text = inputBoxZhuYinSongs.Text.Substring(0, inputBoxZhuYinSongs.Text.Length - 1); } } /// /// 初始化「清除」按鈕,並從設定檔載入其位置與圖片資源。 /// private void InitializeClearButtonZhuYinSongs() { // 1. 從設定檔 (config.ini) 讀取配置數據 var data = LoadConfigData(); // 2. 讀取「清除」按鈕的座標設定 (從 "SpecialButtonCoordinates" 內的 "clearButtonZhuYinSongs" 取得) clearButtonZhuYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "clearButtonZhuYinSongs"); // 3. 讀取「清除」按鈕的圖片 (正常、滑鼠懸停、按下狀態) var buttonImages = LoadButtonImages(data, "ClearButtonImagesZhuYin"); // 4. 建立「清除」按鈕,並設定對應的事件處理函式 (ClearButtonZhuYinSongs_Click) clearButtonZhuYinSongs = CreateSpecialButton( "btnClearZhuYinSongs", // 按鈕名稱 clearButtonZhuYinCoords, // 按鈕座標與大小 buttonImages.normal, // 正常狀態圖片 buttonImages.mouseOver, // 滑鼠懸停圖片 buttonImages.mouseDown, // 按下時圖片 ClearButtonZhuYinSongs_Click // 點擊事件處理函式 ); } /// /// 當使用者點擊「清除」按鈕時,將輸入框 (inputBoxZhuYinSongs) 的內容清空。 /// /// 觸發事件的按鈕。 /// 事件參數。 private void ClearButtonZhuYinSongs_Click(object sender, EventArgs e) { // 1. 確保視窗內包含「注音輸入框」(inputBoxZhuYinSongs),且輸入框內有文字 if (this.Controls.Contains(inputBoxZhuYinSongs) && inputBoxZhuYinSongs.Text.Length > 0) { // 2. 清空輸入框內容 inputBoxZhuYinSongs.Text = ""; } } /// /// 初始化注音輸入的關閉按鈕,從設定檔讀取按鈕座標與圖片,並設置點擊事件 /// private void InitializeCloseButtonZhuYinSongs() { // 讀取設定檔數據 var data = LoadConfigData(); // 從設定檔讀取關閉按鈕的座標 closeButtonZhuYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "closeButtonZhuYinSongs"); // 從設定檔讀取關閉按鈕的圖片 var buttonImages = LoadButtonImages(data, "CloseButtonImagesZhuYin"); // 創建關閉按鈕並綁定點擊事件 closeButtonZhuYinSongs = CreateSpecialButton( "btnCloseZhuYinSongs", // 按鈕名稱 closeButtonZhuYinCoords, // 按鈕座標 buttonImages.normal, // 正常狀態圖片 buttonImages.mouseOver, // 滑鼠懸停圖片 buttonImages.mouseDown, // 按下圖片 CloseButtonZhuYinSongs_Click // 綁定點擊事件 ); } /// /// 關閉注音輸入介面,隱藏相關 UI 元件 /// /// 觸發事件的按鈕 (關閉按鈕) /// 事件參數 private void CloseButtonZhuYinSongs_Click(object sender, EventArgs e) { // 隱藏注音輸入的圖片 pictureBoxZhuYinSongs.Visible = false; zhuyinSearchSongButton.BackgroundImage = zhuyinSearchSongNormalBackground; // 隱藏注音輸入的所有按鈕與介面元素 SetZhuYinSongsAndButtonsVisibility(false); } /// /// 初始化注音輸入框 (RichTextBox),設定外觀、事件處理及位置大小 /// private void InitializeInputBoxZhuYinSongs() { try { // 讀取輸入框的配置,例如字體、顏色、大小等 LoadInputBoxConfig(); // 建立注音輸入框並套用讀取到的設定 inputBoxZhuYinSongs = new RichTextBox { Name = "inputBoxZhuYinSongs", ForeColor = inputBoxForeColor, // 設定文字顏色 Font = new Font(inputBoxFontName, inputBoxFontSize, inputBoxFontStyle), // 設定字體 ScrollBars = RichTextBoxScrollBars.None // 禁用滾動條 }; // 調整輸入框大小與位置 ResizeAndPositionControl(inputBoxZhuYinSongs, inputBoxZhuYinCoords.X, inputBoxZhuYinCoords.Y, inputBoxZhuYinCoords.Width, inputBoxZhuYinCoords.Height); // 設定文字變更事件,用來即時篩選歌曲 inputBoxZhuYinSongs.TextChanged += (sender, e) => { string searchText = inputBoxZhuYinSongs.Text; // 取得輸入內容 // 根據輸入的注音篩選歌曲清單 var searchResults = allSongs.Where(song => song.PhoneticNotation.StartsWith(searchText)).ToList(); // 重置分頁 currentPage = 0; currentSongList = searchResults; totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage); // 更新多頁面面板的內容 multiPagePanel.currentPageIndex = 0; multiPagePanel.LoadSongs(currentSongList); }; // 將輸入框加入到 UI 控制項 this.Controls.Add(inputBoxZhuYinSongs); } catch (Exception ex) { Console.WriteLine("Error initializing inputBoxZhuYinSongs: " + ex.Message); } } /// /// 存儲 PictureBoxZhuYinSongs 的座標與尺寸信息。 /// /// /// 此元組包含以下四個值: /// X:X 座標 /// , Y:Y 座標 /// , Width:寬度 /// , Height:高度 /// private (int X, int Y, int Width, int Height) pictureBoxZhuYinSongCoords; /// /// 從設定檔 (config.ini) 讀取 PictureBoxZhuYinSongs 的座標與尺寸 /// private void LoadPictureBoxZhuYinSongCoordsFromConfig() { // 建立 INI 檔案解析器 var parser = new FileIniDataParser(); // 讀取 config.ini 設定檔的內容 IniData data = parser.ReadFile("config.ini"); // 取得 "PictureBoxZhuYinSongs" 段落的設定數據 var coords = data["PictureBoxZhuYinSongs"]; // 解析座標與尺寸,並存入 pictureBoxZhuYinSongCoords pictureBoxZhuYinSongCoords = ( int.Parse(coords["X"]), // 讀取 X 座標 int.Parse(coords["Y"]), // 讀取 Y 座標 int.Parse(coords["Width"]), // 讀取寬度 int.Parse(coords["Height"]) // 讀取高度 ); } /// /// 在 pictureBoxZhuYinSongs 上顯示指定路徑的圖片,並根據設定調整其大小與位置。 /// /// 要顯示的圖片檔案路徑 private void ShowImageOnPictureBoxZhuYinSongs(string imagePath) { // 從 config.ini 讀取 PictureBox 的座標與尺寸 LoadPictureBoxZhuYinSongCoordsFromConfig(); // 讀取圖片檔案並載入 Bitmap 物件 Bitmap originalImage = new Bitmap(imagePath); // 設定圖片顯示區域,使用從設定檔讀取的座標與尺寸 Rectangle displayArea = new Rectangle( pictureBoxZhuYinSongCoords.X, pictureBoxZhuYinSongCoords.Y, pictureBoxZhuYinSongCoords.Width, pictureBoxZhuYinSongCoords.Height ); // 設定 PictureBox 的圖片 pictureBoxZhuYinSongs.Image = originalImage; // 調整 PictureBox 的大小與位置,使其符合設定 ResizeAndPositionPictureBox( pictureBoxZhuYinSongs, displayArea.X, displayArea.Y, displayArea.Width, displayArea.Height ); // 顯示 PictureBox pictureBoxZhuYinSongs.Visible = true; } /// /// 設定注音歌曲相關的 PictureBox、按鈕和輸入框的可見性。 /// /// 若為 true,則顯示這些控件;否則隱藏。 private void SetZhuYinSongsAndButtonsVisibility(bool isVisible) { // 定義要執行的操作,設定各控件的可見性 System.Action action = () => { try { // 暫停佈局邏輯,防止在調整控件可見性時觸發不必要的佈局計算,提升性能 SuspendLayout(); // 檢查並設定 pictureBoxZhuYinSongs 的可見性 if (pictureBoxZhuYinSongs == null) { Console.WriteLine("pictureBoxZhuYinSongs is null"); } else { pictureBoxZhuYinSongs.Visible = isVisible; if (isVisible) pictureBoxZhuYinSongs.BringToFront(); } // 檢查並設定 phoneticButtonsForSongs 陣列中每個按鈕的可見性 if (phoneticButtonsForSongs == null) { Console.WriteLine("phoneticButtonsForSongs is null"); } else { foreach (var button in phoneticButtonsForSongs) { if (button == null) { Console.WriteLine("One of the phoneticButtonsForSongs is null"); } else { button.Visible = isVisible; if (isVisible) button.BringToFront(); } } } // 檢查並設定 modifyButtonZhuYinSongs 的可見性 if (modifyButtonZhuYinSongs == null) { Console.WriteLine("modifyButtonZhuYinSongs is null"); } else { modifyButtonZhuYinSongs.Visible = isVisible; if (isVisible) modifyButtonZhuYinSongs.BringToFront(); } // 檢查並設定 clearButtonZhuYinSongs 的可見性 if (clearButtonZhuYinSongs == null) { Console.WriteLine("clearButtonZhuYinSongs is null"); } else { clearButtonZhuYinSongs.Visible = isVisible; if (isVisible) clearButtonZhuYinSongs.BringToFront(); } // 檢查並設定 closeButtonZhuYinSongs 的可見性 if (closeButtonZhuYinSongs == null) { Console.WriteLine("closeButtonZhuYinSongs is null"); } else { closeButtonZhuYinSongs.Visible = isVisible; if (isVisible) closeButtonZhuYinSongs.BringToFront(); } // 檢查並設定 inputBoxZhuYinSongs 的可見性 if (inputBoxZhuYinSongs == null) { Console.WriteLine("inputBoxZhuYinSongs is null"); } else { inputBoxZhuYinSongs.Visible = isVisible; if (isVisible) inputBoxZhuYinSongs.BringToFront(); } // 恢復佈局邏輯,允許佈局計算 ResumeLayout(); // 強制控件立即執行佈局邏輯,確保佈局更新立即生效 PerformLayout(); // 刷新各控件,確保其狀態立即更新 pictureBoxZhuYinSongs?.Refresh(); if (phoneticButtonsForSongs != null) { foreach (var button in phoneticButtonsForSongs) { button?.Refresh(); } } modifyButtonZhuYinSongs?.Refresh(); clearButtonZhuYinSongs?.Refresh(); closeButtonZhuYinSongs?.Refresh(); inputBoxZhuYinSongs?.Refresh(); } catch (Exception ex) { // 捕捉並輸出異常資訊,方便除錯 Console.WriteLine("Error in SetZhuYinSongsAndButtonsVisibility: " + ex.Message); } }; // 如果當前執行緒需要呼叫 Invoke 方法才能修改控件,則使用 Invoke if (this.InvokeRequired) { this.Invoke(action); } else { action(); } } } }