新版0407

This commit is contained in:
jasonchenwork 2025-04-07 16:54:10 +08:00
parent ca5c4be674
commit 789cca123a
122 changed files with 24686 additions and 0 deletions

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
DualScreenSetup.exe
Superstar.mdf
Superstar_log.ldf
.vs
build.bat
*.exe
bin/*
obj/*
secondary_graph.grf
primary_graph.grf

12
ClickSequenceState.cs Normal file
View File

@ -0,0 +1,12 @@
namespace DualScreenDemo
{
public enum ClickSequenceState
{
Initial,
FirstClicked,
SecondClicked,
ThirdClicked,
Completed
}
}

13
Clsid.cs Normal file
View File

@ -0,0 +1,13 @@
using System;
namespace DualScreenDemo
{
public static class Clsid
{
public static readonly Guid LAVSplitter = new Guid("171252A0-8820-4AFE-9DF8-5C92B2D66B04");
public static readonly Guid LAVVideoDecoder = new Guid("EE30215D-164F-4A92-A4EB-9D4C13390F9F");
public static readonly Guid LAVAudioDecoder = new Guid("E8E73B6B-4CB3-44A4-BE99-4F7BCB96E491");
public static readonly Guid VideoRenderer = new Guid("B87BEB7B-8D29-423F-AE4D-6582C10175AC");
public static readonly Guid AudioSwitcher = new Guid("1367C6F9-3FDD-42E9-AE3A-BC57F4C40D6D");
}
}

14
ComInterop.cs Normal file
View File

@ -0,0 +1,14 @@
using System;
using System.Runtime.InteropServices;
namespace DualScreenDemo
{
public class ComInterop
{
[DllImport("ole32.dll")]
public static extern int CoInitializeEx(IntPtr pvReserved, int dwCoInit);
public const int COINIT_APARTMENTTHREADED = 0x2;
public const int COINIT_MULTITHREADED = 0x0;
}
}

931
CommandHandler.cs Normal file
View File

@ -0,0 +1,931 @@
using System;
using System.Linq;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Diagnostics;
using DBObj;
using OverlayFormObj;
namespace DualScreenDemo
{
public class CommandHandler
{
public static bool readyForSongListInput = false;
private readonly SongListManager songListManager;
public CommandHandler(SongListManager songListManager)
{
this.songListManager = songListManager;
}
public async Task ProcessData(string indata)
{
string filePath = Path.Combine(Application.StartupPath, "dataLog.txt");
if (CheckLogForShutdown(filePath))
{
Console.WriteLine("Shutdown condition met. Application will now close.");
ShutdownComputer();
}
switch (indata)
{
case "A261A4":
HandleInputA();
break;
case "A262A4":
HandleInputB();
break;
case "A263A4":
ClearDisplay();
break;
case "A268A4":
OverlayForm.MainForm.currentPage = 1;
DisplaySongHistory();
break;
case "A26AA4":
PreviousPage();
break;
case "A26BA4":
NextPage();
break;
case "A271A4":
HandleNewSongAnnouncements();
break;
case "A273A4":
HandleHotSongAnnouncements();
break;
case "A267A4":
SkipToNextSong();
ClearDisplay();
break;
case "A269A4":
ReplayCurrentSong();
break;
// 原唱
case "A26CA4":
Console.WriteLine("ToggleVocalRemoval Invoked");
InvokeAction(() => VideoPlayerForm.Instance.ToggleVocalRemoval());
InvokeAction(() => OverlayForm.MainForm.ShowOriginalSongLabel());
break;
// 導唱
case "A26EA4":
InvokeAction(() => VideoPlayerForm.Instance.ToggleVocalRemoval());
break;
case "A26DA4":
PauseOrResumeSong();
break;
case "A276A4":
ToggleMute();
break;
case "A274A4":
HandleArtistAnnouncements();
break;
case "A2B3A4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowVolumeUpLabel());
break;
case "A2B4A4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowVolumeDownLabel());
break;
case "A2B5A4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowMicUpLabel());
break;
case "A2B6A4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowMicDownLabel());
break;
case "A2C2A4":
InvokeAction(() => OverlayForm.MainForm.HidemicLabels());
InvokeAction(() => OverlayForm.MainForm.ShowStandardLabel());
break;
case "A2C3A4":
InvokeAction(() => OverlayForm.MainForm.HidemicLabels());
InvokeAction(() => OverlayForm.MainForm.ShowProfessionalLabel());
break;
case "A2C4A4":
InvokeAction(() => OverlayForm.MainForm.HidemicLabels());
InvokeAction(() => OverlayForm.MainForm.ShowSquareLabel());
break;
case "A2C1A4":
InvokeAction(() => OverlayForm.MainForm.HidemicLabels());
InvokeAction(() => OverlayForm.MainForm.ShowSingDownLabel());
break;
case "A2D5A4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowBrightLabel());
break;
case "A2D7A4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowRomanticLabel());
break;
/* case "A27CA4":
InvokeAction(() => OverlayForm.MainForm.ShowMaleKeyLabel());
break;
case "A282A4":
InvokeAction(() => OverlayForm.MainForm.ShowFemaleKeyLabel());
break;*/
case "A2D6A4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowSoftLabel());
break;
case "A2D8A4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowDynamicLabel());
break;
case "A275A4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowTintLabel());
break;
case "A283A4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowKeyUpLabel("↑升4調"));
break;
case "A282A4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowKeyUpLabel("↑升3調"));
break;
case "A281A4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowKeyUpLabel("↑升2調"));
break;
case "A280A4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowKeyUpLabel("↑升1調"));
break;
case "A27FA4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowStandardKeyLabel());
break;
case "A27EA4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowKeyDownLabel("↓降1調"));
break;
case "A27DA4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowKeyDownLabel("↓降2調"));
break;
case "A27CA4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowKeyDownLabel("↓降3調"));
break;
case "A27BA4":
InvokeAction(() => OverlayForm.MainForm.HideAllLabels());
InvokeAction(() => OverlayForm.MainForm.ShowKeyDownLabel("↓降4調"));
break;
default:
if (Regex.IsMatch(indata, @"^A23\d+A4$"))
{
HandleNumberInput(indata);
}
break;
}
}
void InvokeAction(Action action)
{
if (OverlayForm.MainForm.InvokeRequired)
{
OverlayForm.MainForm.Invoke(action);
}
else
{
action();
}
}
private static void SkipToNextSong()
{
if (PrimaryForm.Instance.InvokeRequired)
{
PrimaryForm.Instance.Invoke(new System.Action(() => PrimaryForm.Instance.videoPlayerForm.SkipToNextSong()));
}
else
{
PrimaryForm.Instance.videoPlayerForm.SkipToNextSong();
}
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
OverlayForm.MainForm.ShowStandardLabel();
}));
}
private static void ReplayCurrentSong()
{
if (PrimaryForm.Instance.InvokeRequired)
{
PrimaryForm.Instance.Invoke(new System.Action(() => PrimaryForm.Instance.videoPlayerForm.ReplayCurrentSong()));
}
else
{
PrimaryForm.Instance.videoPlayerForm.ReplayCurrentSong();
}
}
private static void PauseOrResumeSong()
{
if (PrimaryForm.Instance.InvokeRequired)
{
PrimaryForm.Instance.Invoke(new System.Action(() => PrimaryForm.Instance.videoPlayerForm.PauseOrResumeSong()));
}
else
{
PrimaryForm.Instance.videoPlayerForm.PauseOrResumeSong();
}
}
public static void ToggleMute()
{
if (VideoPlayerForm.Instance.InvokeRequired)
{
VideoPlayerForm.Instance.Invoke(new System.Action(ToggleMute));
}
else
{
if (VideoPlayerForm.Instance.isMuted)
{
VideoPlayerForm.Instance.SetVolume(VideoPlayerForm.Instance.previousVolume);
VideoPlayerForm.Instance.isMuted = false;
OverlayForm.MainForm.Invoke(new System.Action(() => OverlayForm.MainForm.HideMuteLabel()));
}
else
{
VideoPlayerForm.Instance.previousVolume = VideoPlayerForm.Instance.GetVolume();
VideoPlayerForm.Instance.SetVolume(-10000);
VideoPlayerForm.Instance.isMuted = true;
OverlayForm.MainForm.Invoke(new System.Action(() => OverlayForm.MainForm.ShowMuteLabel()));
}
}
}
private void HandleInputA()
{
OverlayForm.displayTimer.Stop();
string input = "a";
string songNumber = OverlayForm.ReadSongNumber();
var song = songListManager.SearchSongByNumber(songNumber);
if (readyForSongListInput)
{
if (OverlayForm.MainForm.InvokeRequired)
{
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
OverlayForm.MainForm.OnUserInput(input);
}));
}
else
{
OverlayForm.MainForm.OnUserInput(input);
}
}
else
{
if (OverlayForm.MainForm.InvokeRequired)
{
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
if (song != null)
{
ClearDisplay();
OverlayForm.MainForm.displayLabel.Text = String.Format("已點歌曲:{0}", song);
OverlayForm.MainForm.AddSongToPlaylist(song);
OverlayForm.MainForm.nextSongLabel.Visible = false;
OverlayForm.displayTimer.Start();
}
else
{
ClearDisplay();
OverlayForm.MainForm.displayLabel.Text = "輸入錯誤!!!";
OverlayForm.MainForm.nextSongLabel.Visible = false;
OverlayForm.displayTimer.Start();
}
}));
}
else
{
if (song != null)
{
ClearDisplay();
OverlayForm.MainForm.displayLabel.Text = String.Format("{0}", song);
OverlayForm.MainForm.AddSongToPlaylist(song);
OverlayForm.MainForm.nextSongLabel.Visible = false;
OverlayForm.displayTimer.Start();
}
else
{
ClearDisplay();
OverlayForm.MainForm.displayLabel.Text = "輸入錯誤!!!";
OverlayForm.MainForm.nextSongLabel.Visible = false;
OverlayForm.displayTimer.Start();
}
}
}
}
private void HandleInputB()
{
OverlayForm.displayTimer.Stop();
string input = "b";
string songNumber = OverlayForm.ReadSongNumber();
var song = songListManager.SearchSongByNumber(songNumber);
if (readyForSongListInput)
{
if (OverlayForm.MainForm.InvokeRequired)
{
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
OverlayForm.MainForm.OnUserInput(input);
}));
}
else
{
OverlayForm.MainForm.OnUserInput(input);
}
}
else
{
if (OverlayForm.MainForm.InvokeRequired)
{
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
if (song != null)
{
ClearDisplay();
OverlayForm.MainForm.displayLabel.Text = String.Format("插播歌曲{0}", song);
OverlayForm.MainForm.InsertSongToPlaylist(song);
OverlayForm.MainForm.nextSongLabel.Visible = false;
OverlayForm.displayTimer.Start();
}
else
{
ClearDisplay();
OverlayForm.MainForm.displayLabel.Text = "輸入錯誤!!!";
OverlayForm.MainForm.nextSongLabel.Visible = false;
OverlayForm.displayTimer.Start();
}
}));
}
else
{
if (song != null)
{
ClearDisplay();
OverlayForm.MainForm.displayLabel.Text = String.Format("已點歌曲:{0}", song);
OverlayForm.MainForm.nextSongLabel.Visible = false;
}
else
{
ClearDisplay();
OverlayForm.MainForm.displayLabel.Text = "輸入錯誤!!!";
OverlayForm.MainForm.nextSongLabel.Visible = false;
OverlayForm.displayTimer.Start();
}
}
}
}
private static void ClearDisplay()
{
OverlayForm.displayTimer.Stop();
if (OverlayForm.MainForm.InvokeRequired)
{
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
foreach (var control in OverlayForm.MainForm.Controls.OfType<Control>().ToArray())
{
if (control != OverlayForm.MainForm.displayLabel &&
control != OverlayForm.MainForm.pauseLabel &&
control != OverlayForm.MainForm.muteLabel &&
control != OverlayForm.MainForm.volumeUpLabel &&
control != OverlayForm.MainForm.volumeDownLabel &&
control != OverlayForm.MainForm.micUpLabel &&
control != OverlayForm.MainForm.micDownLabel &&
control != OverlayForm.MainForm.standardKeyLabel &&
control != OverlayForm.MainForm.keyUpLabel &&
control != OverlayForm.MainForm.keyDownLabel &&
control != OverlayForm.MainForm.maleKeyLabel &&
control != OverlayForm.MainForm.femaleKeyLabel &&
control != OverlayForm.MainForm.squareLabel &&
control != OverlayForm.MainForm.professionalLabel &&
control != OverlayForm.MainForm.standardLabel &&
control != OverlayForm.MainForm.singDownLabel &&
control != OverlayForm.MainForm.brightLabel &&
control != OverlayForm.MainForm.softLabel &&
control != OverlayForm.MainForm.autoLabel &&
control != OverlayForm.MainForm.romanticLabel &&
control != OverlayForm.MainForm.dynamicLabel &&
control != OverlayForm.MainForm.tintLabel &&
control != OverlayForm.MainForm.blackBackgroundPanel &&
control != OverlayForm.MainForm.nextSongLabel)
{
OverlayForm.MainForm.Controls.Remove(control);
control.Dispose();
}
}
OverlayForm.MainForm.displayLabel.Text = "";
readyForSongListInput = false;
OverlayForm.SetUIState(OverlayForm.UIState.Initial);
Console.WriteLine(OverlayForm.MainForm.displayLabel.Text);
}));
}
else
{
foreach (var control in OverlayForm.MainForm.Controls.OfType<Control>().ToArray())
{
if (control != OverlayForm.MainForm.displayLabel &&
control != OverlayForm.MainForm.pauseLabel &&
control != OverlayForm.MainForm.muteLabel &&
control != OverlayForm.MainForm.volumeUpLabel &&
control != OverlayForm.MainForm.volumeDownLabel &&
control != OverlayForm.MainForm.micUpLabel &&
control != OverlayForm.MainForm.micDownLabel &&
control != OverlayForm.MainForm.standardKeyLabel &&
control != OverlayForm.MainForm.keyUpLabel &&
control != OverlayForm.MainForm.keyDownLabel &&
control != OverlayForm.MainForm.maleKeyLabel &&
control != OverlayForm.MainForm.femaleKeyLabel &&
control != OverlayForm.MainForm.squareLabel &&
control != OverlayForm.MainForm.professionalLabel &&
control != OverlayForm.MainForm.standardLabel &&
control != OverlayForm.MainForm.singDownLabel &&
control != OverlayForm.MainForm.brightLabel &&
control != OverlayForm.MainForm.softLabel &&
control != OverlayForm.MainForm.autoLabel &&
control != OverlayForm.MainForm.romanticLabel &&
control != OverlayForm.MainForm.dynamicLabel &&
control != OverlayForm.MainForm.tintLabel &&
control != OverlayForm.MainForm.blackBackgroundPanel &&
control != OverlayForm.MainForm.nextSongLabel)
{
OverlayForm.MainForm.Controls.Remove(control);
control.Dispose();
}
}
OverlayForm.MainForm.displayLabel.Text = "";
Console.WriteLine("ClearDisplay called.");
readyForSongListInput = false;
OverlayForm.SetUIState(OverlayForm.UIState.Initial);
Console.WriteLine(OverlayForm.MainForm.displayLabel.Text);
}
}
private static void DisplaySongHistory()
{
ClearDisplay();
// 設定總歌曲數量
OverlayForm.MainForm.totalSongs = PrimaryForm.playedSongsHistory.Count;
// 如果播放歷史為空
if (OverlayForm.MainForm.totalSongs == 0)
{
Console.WriteLine("No song history available.");
return;
}
// 計算總頁數
int totalPages = (int)Math.Ceiling(OverlayForm.MainForm.totalSongs / (double)OverlayForm.MainForm.songsPerPage);
int startIndex = (OverlayForm.MainForm.currentPage - 1) * OverlayForm.MainForm.songsPerPage;
int endIndex = Math.Min(startIndex + OverlayForm.MainForm.songsPerPage, OverlayForm.MainForm.totalSongs);
// 準備傳遞給 UpdateHistoryLabel 的數據
List<SongData> historySongs = new List<SongData>();
List<PlayState> playStates = new List<PlayState>();
for (int i = startIndex; i < endIndex; i++)
{
historySongs.Add(PrimaryForm.playedSongsHistory[i]);
playStates.Add(PrimaryForm.playStates[i]);
}
// 調用 UpdateHistoryLabel 顯示數據
if (OverlayForm.MainForm.InvokeRequired)
{
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
OverlayForm.MainForm.UpdateHistoryLabel(historySongs, playStates, OverlayForm.MainForm.currentPage, totalPages);
OverlayForm.MainForm.nextSongLabel.Visible = false;
}));
}
else
{
OverlayForm.MainForm.UpdateHistoryLabel(historySongs, playStates, OverlayForm.MainForm.currentPage, totalPages);
OverlayForm.MainForm.nextSongLabel.Visible = false;
}
// 設定 UI 狀態
OverlayForm.SetUIState(OverlayForm.UIState.PlayHistory);
}
private static void PreviousPage()
{
if (OverlayForm.CurrentUIState == OverlayForm.UIState.SelectingSong)
{
if (OverlayForm.MainForm.InvokeRequired)
{
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
OverlayForm.MainForm.PreviousPage();
}));
}
else
{
OverlayForm.MainForm.PreviousPage();
}
}
else if (OverlayForm.CurrentUIState == OverlayForm.UIState.SelectingArtist)
{
if (OverlayForm.MainForm.InvokeRequired)
{
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
OverlayForm.MainForm.PreviousPage();
}));
}
else
{
OverlayForm.MainForm.PreviousPage();
}
}
else if (OverlayForm.CurrentUIState == OverlayForm.UIState.PlayHistory)
{
if (OverlayForm.MainForm.currentPage > 1)
{
OverlayForm.MainForm.currentPage--;
if (OverlayForm.MainForm.InvokeRequired)
{
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
DisplaySongHistory();
}));
}
else
{
DisplaySongHistory();
}
}
}
else
{
Console.WriteLine("Page turning is not allowed in the current state.");
}
}
private static void NextPage()
{
if (OverlayForm.CurrentUIState == OverlayForm.UIState.SelectingSong)
{
if (OverlayForm.MainForm.InvokeRequired)
{
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
OverlayForm.MainForm.NextPage();
}));
}
else
{
OverlayForm.MainForm.NextPage();
}
}
else if (OverlayForm.CurrentUIState == OverlayForm.UIState.SelectingArtist)
{
if (OverlayForm.MainForm.InvokeRequired)
{
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
OverlayForm.MainForm.NextPage();
}));
}
else
{
OverlayForm.MainForm.NextPage();
}
}
else if (OverlayForm.CurrentUIState == OverlayForm.UIState.PlayHistory)
{
if (OverlayForm.MainForm.currentPage * OverlayForm.MainForm.songsPerPage < OverlayForm.MainForm.totalSongs)
{
OverlayForm.MainForm.currentPage++;
if (OverlayForm.CurrentUIState == OverlayForm.UIState.PlayHistory)
{
if (OverlayForm.MainForm.InvokeRequired)
{
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
DisplaySongHistory();
}));
}
else
{
DisplaySongHistory();
}
}
}
}
else
{
Console.WriteLine("Page turning is not allowed in the current state.");
}
}
private static void HandleNewSongAnnouncements()
{
ClearDisplay();
OverlayForm.CurrentCategory = OverlayForm.Category.NewSongs;
string[] messages = new string[]
{
"新歌快訊",
"1. 國語",
"2. 台語",
"3. 粵語",
"4. 英語",
"5. 日語",
"6. 韓語",
};
readyForSongListInput = true;
OverlayForm.SetUIState(OverlayForm.UIState.SelectingLanguage);
UpdateDisplayLabels(messages);
}
private static void HandleHotSongAnnouncements()
{
ClearDisplay();
OverlayForm.CurrentCategory = OverlayForm.Category.HotSongs;
string[] messages = new string[]
{
"熱門排行",
"1. 國語",
"2. 台語",
"3. 粵語",
"4. 英語",
"5. 日語",
"6. 韓語",
};
readyForSongListInput = true;
OverlayForm.SetUIState(OverlayForm.UIState.SelectingLanguage);
UpdateDisplayLabels(messages);
}
private static void HandleArtistAnnouncements()
{
ClearDisplay();
OverlayForm.CurrentCategory = OverlayForm.Category.Artists;
string[] messages = new string[]
{
"歌星選歌",
"1.男歌星",
"2.女歌星",
"3.團體",
"4.外語",
"5.全部",
};
readyForSongListInput = true;
OverlayForm.SetUIState(OverlayForm.UIState.SelectingArtistCategory);
UpdateDisplayLabels(messages);
}
private static void UpdateDisplayLabels(string[] messages)
{
if (OverlayForm.MainForm.InvokeRequired)
{
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
foreach (var control in OverlayForm.MainForm.Controls.OfType<Control>().ToArray())
{
if (control != OverlayForm.MainForm.displayLabel &&
control != OverlayForm.MainForm.pauseLabel &&
control != OverlayForm.MainForm.muteLabel &&
control != OverlayForm.MainForm.volumeUpLabel &&
control != OverlayForm.MainForm.volumeDownLabel &&
control != OverlayForm.MainForm.micUpLabel &&
control != OverlayForm.MainForm.micDownLabel &&
control != OverlayForm.MainForm.standardKeyLabel &&
control != OverlayForm.MainForm.keyUpLabel &&
control != OverlayForm.MainForm.keyDownLabel &&
control != OverlayForm.MainForm.maleKeyLabel &&
control != OverlayForm.MainForm.femaleKeyLabel &&
control != OverlayForm.MainForm.squareLabel &&
control != OverlayForm.MainForm.professionalLabel &&
control != OverlayForm.MainForm.standardLabel &&
control != OverlayForm.MainForm.singDownLabel &&
control != OverlayForm.MainForm.brightLabel &&
control != OverlayForm.MainForm.softLabel &&
control != OverlayForm.MainForm.autoLabel &&
control != OverlayForm.MainForm.romanticLabel &&
control != OverlayForm.MainForm.dynamicLabel &&
control != OverlayForm.MainForm.tintLabel &&
control != OverlayForm.MainForm.blackBackgroundPanel &&
control != OverlayForm.MainForm.nextSongLabel)
{
OverlayForm.MainForm.Controls.Remove(control);
control.Dispose();
}
}
OverlayForm.MainForm.UpdateDisplayLabels(messages);
}));
}
else
{
foreach (var control in OverlayForm.MainForm.Controls.OfType<Control>().ToArray())
{
if (control != OverlayForm.MainForm.displayLabel &&
control != OverlayForm.MainForm.pauseLabel &&
control != OverlayForm.MainForm.muteLabel &&
control != OverlayForm.MainForm.volumeUpLabel &&
control != OverlayForm.MainForm.volumeDownLabel &&
control != OverlayForm.MainForm.micUpLabel &&
control != OverlayForm.MainForm.micDownLabel &&
control != OverlayForm.MainForm.standardKeyLabel &&
control != OverlayForm.MainForm.keyUpLabel &&
control != OverlayForm.MainForm.keyDownLabel &&
control != OverlayForm.MainForm.maleKeyLabel &&
control != OverlayForm.MainForm.femaleKeyLabel &&
control != OverlayForm.MainForm.squareLabel &&
control != OverlayForm.MainForm.professionalLabel &&
control != OverlayForm.MainForm.standardLabel &&
control != OverlayForm.MainForm.singDownLabel &&
control != OverlayForm.MainForm.brightLabel &&
control != OverlayForm.MainForm.softLabel &&
control != OverlayForm.MainForm.autoLabel &&
control != OverlayForm.MainForm.romanticLabel &&
control != OverlayForm.MainForm.dynamicLabel &&
control != OverlayForm.MainForm.tintLabel &&
control != OverlayForm.MainForm.blackBackgroundPanel &&
control != OverlayForm.MainForm.nextSongLabel)
{
OverlayForm.MainForm.Controls.Remove(control);
control.Dispose();
}
}
OverlayForm.MainForm.UpdateDisplayLabels(messages);
}
}
private static void HandleNumberInput(string trimmedData)
{
string number = trimmedData;
var match = Regex.Match(trimmedData, @"^A23(\d)A4$");
if (match.Success)
{
number = match.Groups[1].Value;
Console.WriteLine($"Handling number: {number}");
}
if (readyForSongListInput)
{
if (OverlayForm.MainForm.InvokeRequired)
{
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
OverlayForm.MainForm.OnUserInput(number);
}));
}
else
{
OverlayForm.MainForm.OnUserInput(number);
}
}
else
{
if (OverlayForm.MainForm.InvokeRequired)
{
OverlayForm.MainForm.Invoke(new System.Action(() =>
{
OverlayForm.DisplayNumberAtTopLeft(number);
OverlayForm.MainForm.HideAllLabels();
}));
}
else
{
OverlayForm.DisplayNumberAtTopLeft(number);
OverlayForm.MainForm.HideAllLabels();
}
}
}
public static bool CheckLogForShutdown(string filePath)
{
if (File.Exists(filePath))
{
string content = File.ReadAllText(filePath).Replace(Environment.NewLine, "");
if (content.Length >= 6 && content.Substring(content.Length - 6) == "bbbaaa")
{
return true;
}
}
return false;
}
public static void ShutdownComputer()
{
try
{
ProcessStartInfo processStartInfo = new ProcessStartInfo("cmd.exe", "/c shutdown /s /f /t 0")
{
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true
};
Process process = Process.Start(processStartInfo);
process.WaitForExit();
Console.WriteLine("Computer is shutting down...");
}
catch (Exception ex)
{
Console.WriteLine("Error shutting down computer: " + ex.Message);
}
}
}
}

33
DBObj/Artist.cs Normal file
View File

@ -0,0 +1,33 @@
namespace DBObj
{
// artist OOP test
public class Artist
{
public string Name { get; set; }
public string Phonetic { get; set; }
public string Category { get; set; }
public int Strokes { get; set; }
public Artist(string name, string phonetic, string category, int strokes)
{
Name = name;
Phonetic = phonetic;
Category = category;
Strokes = strokes;
}
public override string ToString()
{
return $"Name: {Name}, Phonetic: {Phonetic}, Category: {Category}, Strokes: {Strokes}";
}
}
}

116
DBObj/ArtistManager.cs Normal file
View File

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using DualScreenDemo;
namespace DBObj
{
/**
*/
public class ArtistManager
{
private static ArtistManager _instance;
public List<Artist> AllArtists { get; private set; }
public static ArtistManager Instance
{
get
{
if (_instance == null)
{
_instance = new ArtistManager();
}
return _instance;
}
}
public ArtistManager()
{
AllArtists = new List<Artist>();
LoadArtists();
}
private void LoadArtists()
{
string databaseFileName = "KSongDatabase.db";
string databasePath = Path.Combine(Application.StartupPath, databaseFileName);
Console.WriteLine(databasePath);
string connectionString = String.Format("Data Source={0};Version=3;", databasePath);
using (var connection = new SQLiteConnection(connectionString))
{
try
{
connection.Open();
string sql = "SELECT 歌手姓名, 歌手注音, 歌手分類, 歌手筆畫 FROM ArtistLibrary";
using (var command = new SQLiteCommand(sql, connection))
{
using (SQLiteDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
string artist = reader["歌手姓名"].ToString();
string phonetic = reader["歌手注音"].ToString();
string category = reader["歌手分類"].ToString();
string strokesStr = reader["歌手筆畫"].ToString();
if (string.IsNullOrEmpty(strokesStr))
{
// Console.WriteLine("歌手筆畫的值為空或無效");
}
if (double.TryParse(strokesStr, out double strokesDouble))
{
int strokes = (int)Math.Round(strokesDouble);
AllArtists.Add(new Artist(artist, phonetic, category, strokes));
}
else
{
// Console.WriteLine($"Failed to parse '歌手筆畫' value: {strokesStr}");
}
}
}
}
connection.Close();
}
catch (Exception ex)
{
Console.WriteLine("Failed to load artists from SQLite database: " + ex.Message);
}
}
}
private void PrintAllArtists()
{
Console.WriteLine("All Artists:");
foreach (var artist in AllArtists)
{
Console.WriteLine(artist.ToString());
}
}
public List<Artist> GetArtistsByCategoryAndStrokeCountRange(string category, int minStrokes, int maxStrokes)
{
if (category == "全部")
{
return AllArtists.Where(artist => artist.Strokes >= minStrokes && artist.Strokes <= maxStrokes).ToList();
}
else
{
return AllArtists.Where(artist => artist.Category == category && artist.Strokes >= minStrokes && artist.Strokes <= maxStrokes).ToList();
}
}
}
}

65
DBObj/SongData.cs Normal file
View File

@ -0,0 +1,65 @@
using System;
namespace DBObj
{
public class SongData
{
public string SongNumber { get; set; }
public string Category { get; set; }
public string Song { get; set; }
public double Plays { get; set; }
public string ArtistA { get; set; }
public string ArtistB { get; set; }
public string ArtistACategory { get; set; }
public string ArtistBCategory { get; set; }
public DateTime AddedTime { get; set; }
public string SongFilePathHost1 { get; set; }
public string SongFilePathHost2 { get; set; }
public string PhoneticNotation { get; set; }
public string PinyinNotation { get; set; }
public string ArtistAPhonetic { get; set; }
public string ArtistBPhonetic { get; set; }
public string ArtistASimplified { get; set; }
public string ArtistBSimplified { get; set; }
public string SongSimplified { get; set; }
public string SongGenre { get; set; }
public string ArtistAPinyin { get; set; }
public string ArtistBPinyin { get; set; }
public int HumanVoice { get; set; }
public SongData(string songNumber, string category, string song, double plays, string artistA, string artistB, string artistACategory, string artistBCategory, DateTime addedTime, string songFilePathHost1, string songFilePathHost2, string phoneticNotation, string pinyinNotation, string artistAPhonetic, string artistBPhonetic, string artistASimplified, string artistBSimplified, string songSimplified, string songGenre, string artistAPinyin, string artistBPinyin, int humanVoice)
{
SongNumber = songNumber;
Category = category;
Song = song;
Plays = plays;
ArtistA = artistA;
ArtistB = artistB;
ArtistACategory = artistACategory;
ArtistBCategory = artistBCategory;
AddedTime = addedTime;
SongFilePathHost1 = songFilePathHost1;
SongFilePathHost2 = songFilePathHost2;
PhoneticNotation = phoneticNotation;
PinyinNotation = pinyinNotation;
ArtistAPhonetic = artistAPhonetic;
ArtistBPhonetic = artistBPhonetic;
ArtistASimplified = artistASimplified;
ArtistBSimplified = artistBSimplified;
SongSimplified = songSimplified;
SongGenre = songGenre;
ArtistAPinyin = artistAPinyin;
ArtistBPinyin = artistBPinyin;
HumanVoice = humanVoice;
}
public override string ToString()
{
return !string.IsNullOrWhiteSpace(ArtistB)
? String.Format("{0} - {1} - {2}", ArtistA, ArtistB, Song)
: String.Format("{0} - {1}", ArtistA, Song);
}
}
}

662
DBObj/SongListManager.cs Normal file
View File

@ -0,0 +1,662 @@
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Globalization;
using System.Diagnostics;
using DualScreenDemo;
namespace DBObj
{
public class SongListManager
{
private static SongListManager _instance;
public List<SongData> AllSongs { get; private set; }
public static Dictionary<string, List<SongData>> NewSongLists { get; private set; }
public static Dictionary<string, List<SongData>> HotSongLists { get; private set; }
public List<SongData> FavoriteSongs { get; private set; }
public const int SongsPerPage = 9;
public bool IsUserLoggedIn { get; set; }
public string UserPhoneNumber { get; set; }
public SongListManager()
{
AllSongs = new List<SongData>();
NewSongLists = new Dictionary<string, List<SongData>>();
HotSongLists = new Dictionary<string, List<SongData>>();
FavoriteSongs = new List<SongData>();
// 尝试更新数据库,但无论结果如何都继续运行
TryUpdateDatabase();
// 继续使用可用的数据库(可能是更新后的或原本的本地数据库)
InitializeDatabase();
LoadSongs();
InitializeNewSongLists();
InitializeHotSongLists();
}
private bool TryUpdateDatabase()
{
try
{
// 1. 检查是否能连接到 SVR01
if (!Directory.Exists(@"\\SVR01\SuperstarB"))
{
Console.WriteLine("未連接到SVR使用本地DB");
return true; // 继续使用本地数据库
}
// 2. 比较本地和服务器文件
string localDbPath = Path.Combine(Application.StartupPath, "KSongDatabase.db");
string serverDbPath = @"\\SVR01\SuperstarB\KSongDatabase.db";
if (!File.Exists(localDbPath))
{
Console.WriteLine("本地無db");
}
else
{
FileInfo localFile = new FileInfo(localDbPath);
FileInfo serverFile = new FileInfo(serverDbPath);
if (serverFile.LastWriteTime <= localFile.LastWriteTime)
{
Console.WriteLine("歌單已是最新");
return true;
}
}
// 3. 需要更新时,复制新文件
Process copyProcess = new Process();
copyProcess.StartInfo.FileName = "cmd.exe";
copyProcess.StartInfo.Arguments = "/C copy /Y \\\\SVR01\\SuperstarB\\KSongDatabase.db KSongDatabase.db";
copyProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
copyProcess.StartInfo.CreateNoWindow = true;
copyProcess.Start();
copyProcess.WaitForExit();
if (copyProcess.ExitCode == 0)
{
Console.WriteLine("歌單更新成功");
}
else
{
Console.WriteLine("歌單複製失敗,使用本地歌單");
}
return true;
}
catch (Exception ex)
{
Console.WriteLine($"更新歌單失敗:{ex.Message}");
return true; // 出错时继续使用本地数据库
}
}
public static SongListManager Instance
{
get
{
if (_instance == null)
{
_instance = new SongListManager();
}
return _instance;
}
}
public List<SongData> GetSongsByArtist(string artistName)
{
return AllSongs.Where(song => song.ArtistA == artistName || song.ArtistB == artistName).ToList();
}
public bool CheckIfPhoneNumberExists(string phoneNumber)
{
string databaseFileName = "KSongDatabase.db";
string databasePath = Path.Combine(Application.StartupPath, databaseFileName);
string connectionString = String.Format("Data Source={0};Version=3;", databasePath);
using (var connection = new SQLiteConnection(connectionString))
{
try
{
connection.Open();
string sql = "SELECT COUNT(1) FROM FavoriteSongs WHERE PhoneNumber = @PhoneNumber";
using (var command = new SQLiteCommand(sql, connection))
{
command.Parameters.AddWithValue("@PhoneNumber", phoneNumber);
int count = Convert.ToInt32(command.ExecuteScalar());
return count > 0;
}
}
catch (Exception ex)
{
Console.WriteLine("Failed to check phone number in SQLite database: " + ex.Message);
return false;
}
finally
{
connection.Close();
}
}
}
public void UserLogin(string phoneNumber)
{
IsUserLoggedIn = true;
UserPhoneNumber = phoneNumber;
LoadFavoriteSongs();
Console.WriteLine(String.Format("UserLoggedIn: {0}, PhoneNumber: {1}", IsUserLoggedIn, UserPhoneNumber));
}
public void UserLogout()
{
IsUserLoggedIn = false;
UserPhoneNumber = null;
FavoriteSongs.Clear();
}
public void InitializeDatabase()
{
string databaseFileName = "KSongDatabase.db";
string databasePath = Path.Combine(Application.StartupPath, databaseFileName);
string connectionString = String.Format("Data Source={0};Version=3;", databasePath);
using (var connection = new SQLiteConnection(connectionString))
{
try
{
connection.Open();
string createTableSql = @"
CREATE TABLE IF NOT EXISTS FavoriteSongs (
PhoneNumber TEXT NOT NULL,
SongNumber TEXT NOT NULL,
PRIMARY KEY (PhoneNumber, SongNumber)
);";
using (var command = new SQLiteCommand(createTableSql, connection))
{
command.ExecuteNonQuery();
}
connection.Close();
}
catch (Exception ex)
{
Console.WriteLine("Failed to initialize SQLite database: " + ex.Message);
}
}
}
public void AddNewUser(string phoneNumber)
{
string databaseFileName = "KSongDatabase.db";
string databasePath = Path.Combine(Application.StartupPath, databaseFileName);
string connectionString = String.Format("Data Source={0};Version=3;", databasePath);
using (var connection = new SQLiteConnection(connectionString))
{
try
{
connection.Open();
string checkTableSql = "SELECT name FROM sqlite_master WHERE type='table' AND name='FavoriteSongs';";
using (var checkCommand = new SQLiteCommand(checkTableSql, connection))
{
var result = checkCommand.ExecuteScalar();
if (result == null)
{
throw new Exception("Table 'FavoriteSongs' does not exist.");
}
}
string sql = "INSERT INTO FavoriteSongs (PhoneNumber, SongNumber) VALUES (@PhoneNumber, @SongNumber)";
using (var command = new SQLiteCommand(sql, connection))
{
command.Parameters.AddWithValue("@PhoneNumber", phoneNumber);
command.Parameters.AddWithValue("@SongNumber", "000000");
command.ExecuteNonQuery();
}
connection.Close();
}
catch (Exception ex)
{
Console.WriteLine("Failed to add new user to SQLite database: " + ex.Message);
}
}
}
private void LoadSongs()
{
string databaseFileName = "KSongDatabase.db";
string databasePath = Path.Combine(Application.StartupPath, databaseFileName);
Console.WriteLine(databasePath);
string connectionString = String.Format("Data Source={0};Version=3;", databasePath);
using (var connection = new SQLiteConnection(connectionString))
{
try
{
connection.Open();
string sql = "SELECT 歌曲編號, 語別, 歌曲名稱, 點播次數, [歌星 A], [歌星 B], 新增日期, [路徑 1], [路徑 2], 歌曲檔名, 歌曲注音, 歌曲拼音, 歌星A分類, 歌星B分類, 歌星A注音, 歌星B注音, 歌星A簡體, 歌星B簡體, 歌名簡體, 分類, 歌星A拼音, 歌星B拼音, 人聲 FROM SongLibrary";
using (var command = new SQLiteCommand(sql, connection))
{
using (SQLiteDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
string songNumber = reader["歌曲編號"].ToString();
string category = reader["語別"].ToString();
string song = reader["歌曲名稱"].ToString();
int plays = Convert.ToInt32(reader["點播次數"]);
string artistA = reader["歌星 A"].ToString();
string artistB = reader["歌星 B"].ToString();
string artistACategory = reader["歌星A分類"].ToString();
string artistBCategory = reader["歌星B分類"].ToString();
string dateValue = reader["新增日期"]?.ToString() ?? "";
DateTime addedTime;
try
{
addedTime=DateTime.Parse(dateValue, CultureInfo.InvariantCulture).Date ;
}
catch (System.FormatException)
{
addedTime = DateTime.Today;
}
string basePathHost1 = reader["路徑 1"].ToString();
string basePathHost2 = reader["路徑 2"].ToString();
string fileName = reader["歌曲檔名"].ToString();
string songFilePathHost1 = Path.Combine(basePathHost1, fileName);
string songFilePathHost2 = Path.Combine(basePathHost2, fileName);
string phoneticNotation = reader["歌曲注音"].ToString();
string pinyinNotation = reader["歌曲拼音"].ToString();
string artistAPhonetic = reader["歌星A注音"].ToString();
string artistBPhonetic = reader["歌星B注音"].ToString();
string artistASimplified = reader["歌星A簡體"].ToString();
string artistBSimplified = reader["歌星B簡體"].ToString();
string songSimplified = reader["歌名簡體"].ToString();
string songGenre = reader["分類"].ToString();
string artistAPinyin = reader["歌星A拼音"].ToString();
string artistBPinyin = reader["歌星B拼音"].ToString();
int humanVoice = Convert.ToInt32(reader["人聲"]);
AllSongs.Add(new SongData(songNumber, category, song, plays, artistA, artistB, artistACategory, artistBCategory, addedTime, songFilePathHost1, songFilePathHost2, phoneticNotation, pinyinNotation, artistAPhonetic, artistBPhonetic, artistASimplified, artistBSimplified, songSimplified, songGenre, artistAPinyin, artistBPinyin, humanVoice));
}
}
}
connection.Close();
}
catch (Exception ex)
{
Console.WriteLine("Failed to load songs from SQLite database: " + ex.Message);
}
}
}
public void LoadFavoriteSongs()
{
if (!IsUserLoggedIn || string.IsNullOrEmpty(UserPhoneNumber))
return;
string databaseFileName = "KSongDatabase.db";
string databasePath = Path.Combine(Application.StartupPath, databaseFileName);
string connectionString = String.Format("Data Source={0};Version=3;", databasePath);
FavoriteSongs.Clear();
FavoriteSongs.Add(new SongData("", "", UserPhoneNumber + " 的歌單", 0, "", "", "", "", DateTime.MinValue, "", "", "", "", "", "", "", "", "", "", "", "", 1));
using (var connection = new SQLiteConnection(connectionString))
{
try
{
connection.Open();
string sql = @"
SELECT
sl., sl., sl., sl.,
sl.[ A], sl.[ B], sl., sl.[ 1],
sl.[ 2], sl., sl., sl.,
sl.A分類, sl.B分類, sl.A注音, sl.B注音,
sl.A簡體, sl.B簡體, sl., sl.,
sl.A拼音, sl.B拼音, sl.
FROM
FavoriteSongs fs
JOIN
SongLibrary sl
ON
fs.SongNumber = sl.
WHERE
fs.PhoneNumber = @PhoneNumber";
using (var command = new SQLiteCommand(sql, connection))
{
command.Parameters.AddWithValue("@PhoneNumber", UserPhoneNumber);
using (SQLiteDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
string songNumber = reader["歌曲編號"].ToString();
string category = reader["語別"].ToString();
string song = reader["歌曲名稱"].ToString();
int plays = Convert.ToInt32(reader["點播次數"]);
string artistA = reader["歌星 A"].ToString();
string artistB = reader["歌星 B"].ToString();
string artistACategory = reader["歌星A分類"].ToString();
string artistBCategory = reader["歌星B分類"].ToString();
DateTime addedTime;
try
{
addedTime = DateTime.ParseExact(reader["新增日期"].ToString(), "yyyy-MM-dd", CultureInfo.InvariantCulture);
}
catch (System.FormatException)
{
addedTime = DateTime.Today;
}
string basePathHost1 = reader["路徑 1"].ToString();
string basePathHost2 = reader["路徑 2"].ToString();
string fileName = reader["歌曲檔名"].ToString();
string songFilePathHost1 = Path.Combine(basePathHost1, fileName);
string songFilePathHost2 = Path.Combine(basePathHost2, fileName);
string phoneticNotation = reader["歌曲注音"].ToString();
string pinyinNotation = reader["歌曲拼音"].ToString();
string artistAPhonetic = reader["歌星A注音"].ToString();
string artistBPhonetic = reader["歌星B注音"].ToString();
string artistASimplified = reader["歌星A簡體"].ToString();
string artistBSimplified = reader["歌星B簡體"].ToString();
string songSimplified = reader["歌名簡體"].ToString();
string songGenre = reader["分類"].ToString();
string artistAPinyin = reader["歌星A拼音"].ToString();
string artistBPinyin = reader["歌星B拼音"].ToString();
int humanVoice = Convert.ToInt32(reader["人聲"]);
FavoriteSongs.Add(new SongData(
songNumber, category, song, plays, artistA, artistB,
artistACategory, artistBCategory, addedTime, songFilePathHost1,
songFilePathHost2, phoneticNotation, pinyinNotation,
artistAPhonetic, artistBPhonetic, artistASimplified,
artistBSimplified, songSimplified, songGenre,
artistAPinyin, artistBPinyin, humanVoice));
}
PrimaryForm.Instance.multiPagePanel.currentPageIndex = 0;
PrimaryForm.Instance.multiPagePanel.LoadSongs(FavoriteSongs);
}
}
connection.Close();
}
catch (Exception ex)
{
Console.WriteLine("Failed to load favorite songs from SQLite database: " + ex.Message);
}
}
}
public void AddToFavorite(string songNumber)
{
if (!IsUserLoggedIn || string.IsNullOrEmpty(UserPhoneNumber))
{
Console.WriteLine("User is not logged in.");
return;
}
string databaseFileName = "KSongDatabase.db";
string databasePath = Path.Combine(Application.StartupPath, databaseFileName);
string connectionString = String.Format("Data Source={0};Version=3;", databasePath);
using (var connection = new SQLiteConnection(connectionString))
{
try
{
connection.Open();
string checkSql = "SELECT COUNT(*) FROM FavoriteSongs WHERE PhoneNumber = @PhoneNumber AND SongNumber = @SongNumber";
using (var checkCommand = new SQLiteCommand(checkSql, connection))
{
checkCommand.Parameters.AddWithValue("@PhoneNumber", UserPhoneNumber);
checkCommand.Parameters.AddWithValue("@SongNumber", songNumber);
long count = (long)checkCommand.ExecuteScalar();
if (count > 0)
{
Console.WriteLine(String.Format("Song {0} is already in favorites.", songNumber));
return;
}
}
string sql = "INSERT INTO FavoriteSongs (PhoneNumber, SongNumber) VALUES (@PhoneNumber, @SongNumber)";
using (var command = new SQLiteCommand(sql, connection))
{
command.Parameters.AddWithValue("@PhoneNumber", UserPhoneNumber);
command.Parameters.AddWithValue("@SongNumber", songNumber);
command.ExecuteNonQuery();
}
connection.Close();
var song = AllSongs.FirstOrDefault(s => s.SongNumber == songNumber);
if (song != null)
{
FavoriteSongs.Add(song);
Console.WriteLine(String.Format("Added song {0} to favorites.", songNumber));
}
else
{
Console.WriteLine(String.Format("Song {0} not found in AllSongs.", songNumber));
}
}
catch (Exception ex)
{
Console.WriteLine("Failed to add song to favorites: " + ex.Message);
}
}
}
public List<SongData> SearchSongsBySinger(string keyword)
{
var keywordLower = keyword.ToLower();
return AllSongs.Where(song => song.ArtistA.ToLower().Contains(keywordLower)
|| song.ArtistB.ToLower().Contains(keywordLower))
.ToList();
}
public List<SongData> SearchSongsByName(string keyword)
{
var keywordLower = keyword.ToLower();
return AllSongs.Where(song => song.Song.ToLower().Contains(keywordLower)).ToList();
}
public SongData SearchSongByNumber(string songNumber)
{
foreach (var song in AllSongs)
{
if (song.SongNumber == songNumber)
{
return song;
}
}
return null;
}
private void InitializeNewSongLists()
{
int songLimit = PrimaryForm.ReadNewSongLimit();
string[] categories = new string[] { "國語", "台語", "粵語", "英文", "日語", "韓語" };
foreach (var category in categories)
{
var songsInCategory = AllSongs
.Where(s => s.Category == category)
.OrderByDescending(s => s.AddedTime)
.Take(songLimit)
.ToList();
NewSongLists.Add(category, songsInCategory);
}
}
private void InitializeHotSongLists()
{
int songLimit = PrimaryForm.ReadHotSongLimit();
string[] categories = new string[] { "國語", "台語", "英文", "日語", "韓語" };
foreach (var category in categories)
{
var songsInCategory = AllSongs
.Where(s => s.Category == category)
.OrderByDescending(s => s.Plays)
.Take(songLimit)
.ToList();
HotSongLists.Add(category, songsInCategory);
}
}
public List<SongData> GetNewSongsByCategory(string category)
{
if (NewSongLists.ContainsKey(category))
return NewSongLists[category];
else
return new List<SongData>();
}
public List<SongData> GetHotSongsByCategory(string category)
{
if (HotSongLists.ContainsKey(category))
return HotSongLists[category];
else
return new List<SongData>();
}
public List<SongData> GetFavoriteSongsByPhoneNumber()
{
List<SongData> favoriteSongs = new List<SongData>();
if (string.IsNullOrEmpty(UserPhoneNumber))
return favoriteSongs;
string databaseFileName = "KSongDatabase.db";
string databasePath = Path.Combine(Application.StartupPath, databaseFileName);
string connectionString = String.Format("Data Source={0};Version=3;", databasePath);
using (var connection = new SQLiteConnection(connectionString))
{
try
{
connection.Open();
string sql = @"
SELECT
sl., sl., sl., sl.,
sl.[ A], sl.[ B], sl., sl.[ 1],
sl.[ 2], sl., sl., sl.,
sl.A分類, sl.B分類, sl.A注音, sl.B注音,
sl.A簡體, sl.B簡體, sl., sl.,
sl.A拼音, sl.B拼音, sl.
FROM
FavoriteSongs fs
JOIN
SongLibrary sl
ON
fs.SongNumber = sl.
WHERE
fs.PhoneNumber = @PhoneNumber";
using (var command = new SQLiteCommand(sql, connection))
{
command.Parameters.AddWithValue("@PhoneNumber", UserPhoneNumber);
using (SQLiteDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
string songNumber = reader["歌曲編號"].ToString();
string category = reader["語別"].ToString();
string song = reader["歌曲名稱"].ToString();
int plays = Convert.ToInt32(reader["點播次數"]);
string artistA = reader["歌星 A"].ToString();
string artistB = reader["歌星 B"].ToString();
string artistACategory = reader["歌星A分類"].ToString();
string artistBCategory = reader["歌星B分類"].ToString();
DateTime addedTime;
try
{
addedTime = DateTime.ParseExact(reader["新增日期"].ToString(), "yyyy-MM-dd", CultureInfo.InvariantCulture);
}
catch (System.FormatException)
{
addedTime = DateTime.Now;
}
string basePathHost1 = reader["路徑 1"].ToString();
string basePathHost2 = reader["路徑 2"].ToString();
string fileName = reader["歌曲檔名"].ToString();
string songFilePathHost1 = Path.Combine(basePathHost1, fileName);
string songFilePathHost2 = Path.Combine(basePathHost2, fileName);
string phoneticNotation = reader["歌曲注音"].ToString();
string pinyinNotation = reader["歌曲拼音"].ToString();
string artistAPhonetic = reader["歌星A注音"].ToString();
string artistBPhonetic = reader["歌星B注音"].ToString();
string artistASimplified = reader["歌星A簡體"].ToString();
string artistBSimplified = reader["歌星B簡體"].ToString();
string songSimplified = reader["歌名簡體"].ToString();
string songGenre = reader["分類"].ToString();
string artistAPinyin = reader["歌星A拼音"].ToString();
string artistBPinyin = reader["歌星B拼音"].ToString();
int humanVoice = Convert.ToInt32(reader["人聲"]);
favoriteSongs.Add(new SongData(
songNumber, category, song, plays, artistA, artistB,
artistACategory, artistBCategory, addedTime, songFilePathHost1,
songFilePathHost2, phoneticNotation, pinyinNotation,
artistAPhonetic, artistBPhonetic, artistASimplified,
artistBSimplified, songSimplified, songGenre,
artistAPinyin, artistBPinyin, humanVoice));
}
}
}
connection.Close();
}
catch (Exception ex)
{
Console.WriteLine("Failed to load favorite songs from SQLite database: " + ex.Message);
}
}
return favoriteSongs;
}
}
}

53
FilterEnumerator.cs Normal file
View File

@ -0,0 +1,53 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using DirectShowLib;
namespace DualScreenDemo
{
public class FilterEnumerator
{
private static readonly Guid IID_IPropertyBag = new Guid("55272A00-42CB-11CE-8135-00AA004BB851");
public void EnumerateFilters()
{
ICreateDevEnum createDevEnum = (ICreateDevEnum)new CreateDevEnum();
IEnumMoniker enumMoniker;
int hr = createDevEnum.CreateClassEnumerator(FilterCategory.LegacyAmFilterCategory, out enumMoniker, 0);
if (hr != 0 || enumMoniker == null)
{
Console.WriteLine("No filters found.");
return;
}
IMoniker[] monikers = new IMoniker[1];
IntPtr fetched = Marshal.AllocHGlobal(sizeof(int));
while (enumMoniker.Next(1, monikers, fetched) == 0)
{
int fetchedCount = Marshal.ReadInt32(fetched);
if (fetchedCount > 0)
{
object objPropBag;
Guid tempGuid = IID_IPropertyBag;
monikers[0].BindToStorage(null, null, ref tempGuid, out objPropBag);
IPropertyBag propBag = objPropBag as IPropertyBag;
object filterName = null;
if (propBag != null)
{
propBag.Read("FriendlyName", out filterName, null);
}
if (filterName != null)
{
Console.WriteLine("Filter: " + filterName.ToString());
}
Marshal.ReleaseComObject(monikers[0]);
}
}
Marshal.ReleaseComObject(enumMoniker);
Marshal.FreeHGlobal(fetched);
}
}
}

10
FormatTypes.cs Normal file
View File

@ -0,0 +1,10 @@
using System;
namespace DualScreenDemo
{
public static class FormatTypes
{
public static readonly Guid VideoInfo = new Guid("05589F80-C356-11CE-BF01-00AA0055595A");
public static readonly Guid WaveEx = new Guid("05589f81-c356-11ce-bf01-00aa0055595a");
}
}

1234
HttpServer.cs Normal file

File diff suppressed because it is too large Load Diff

50
HttpServerManager.cs Normal file
View File

@ -0,0 +1,50 @@
using System.IO;
using System.Linq;
using System.Windows.Forms;
namespace DualScreenDemo
{
public static class HttpServerManager
{
public static async void StartServer()
{
int httpPort = 9090; // 你可以修改此端口
string baseDirectory = Path.Combine(Application.StartupPath, @"themes\superstar\_www");
CleanUpDirectory(baseDirectory);
await HttpServer.StartServer(baseDirectory, httpPort, Program.songListManager);
}
private static void CleanUpDirectory(string baseDirectory)
{
string[] directoriesToKeep = { "css", "fonts", "superstar-pic", "手機點歌" };
var allDirectories = Directory.GetDirectories(baseDirectory);
var allFiles = Directory.GetFiles(baseDirectory);
var filesToKeep = allFiles
.Where(file => file.EndsWith(".html"))
.Select(file => Path.GetFileName(file))
.ToArray();
foreach (var dir in allDirectories)
{
var dirName = Path.GetFileName(dir);
if (!directoriesToKeep.Contains(dirName))
{
Directory.Delete(dir, true);
}
}
foreach (var file in allFiles)
{
var fileName = Path.GetFileName(file);
if (!filesToKeep.Contains(fileName))
{
File.Delete(file);
}
}
}
}
}

32
ImagePanel.cs Normal file
View File

@ -0,0 +1,32 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace DualScreenDemo
{
public class ImagePanel : Panel
{
public ImagePanel()
{
this.DoubleBuffered = true;
this.Size = new Size(1201, 496);
this.AutoSizeMode = AutoSizeMode.GrowAndShrink;
}
public void SetBackgroundImageFromFile(string filePath, Rectangle cropArea)
{
Image image = Image.FromFile(filePath);
Bitmap bmp = new Bitmap(cropArea.Width, cropArea.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
g.DrawImage(image,
new Rectangle(0, 0, bmp.Width, bmp.Height),
cropArea,
GraphicsUnit.Pixel);
}
this.BackgroundImage = bmp;
}
}
}

BIN
Images/ksonglover.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

7
MediaSubTypes.cs Normal file
View File

@ -0,0 +1,7 @@
using System;
public static class MediaSubTypes
{
public static readonly Guid YUY2 = new Guid("32595559-0000-0010-8000-00AA00389B71");
public static readonly Guid IEEE_FLOAT = new Guid("00000003-0000-0010-8000-00AA00389B71");
}

10
MediaTypes.cs Normal file
View File

@ -0,0 +1,10 @@
using System;
namespace DualScreenDemo
{
public static class MediaTypes
{
public static readonly Guid Video = new Guid("73646976-0000-0010-8000-00AA00389B71");
public static readonly Guid Audio = new Guid("73647561-0000-0010-8000-00AA00389B71");
}
}

View File

@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using DualScreenDemo;
namespace OverlayFormObj
{
public partial class OverlayForm
{
private readonly object imageLock = new object();
private void AdjustLabelPositions()
{
int labelHeight = displayLabels.First().Height;
int totalHeight = displayLabels.Count * labelHeight;
int startY = 100;
for (int i = 0; i < displayLabels.Count; i++)
{
Label label = displayLabels[i];
int centerX = (this.Width - label.Width) / 2;
int centerY = startY + i * labelHeight;
label.Location = new Point(centerX, centerY);
}
if (pauseLabel != null)
{
pauseLabel.Location = new Point(this.Width - pauseLabel.Width - 10, 100);
}
if (muteLabel != null)
{
muteLabel.Location = new Point(this.Width - muteLabel.Width - 10, 140);
}
}
public void UpdateMarqueeText(string newText, MarqueeStartPosition startPosition, Color textColor)
{
this.marqueeText = newText;
this.marqueeTextColor = textColor;
// 使用顯示字體進行測量
Font displayFont = new Font("Arial", 25, FontStyle.Bold);
using (Graphics graphics = this.CreateGraphics())
{
SizeF textSize = graphics.MeasureString(marqueeText, displayFont);
int textWidth = (int)textSize.Width;
switch (startPosition)
{
case MarqueeStartPosition.Middle:
this.marqueeXPos = (this.Width / 2) - (textWidth / 2) - 100;
break;
case MarqueeStartPosition.Right:
this.marqueeXPos = this.Width;
break;
}
}
this.Invalidate();
blackBackgroundPanel.Invalidate();
}
public void UpdateMarqueeTextSecondLine(string newText)
{
if (InvokeRequired)
{
Invoke(new MethodInvoker(() => UpdateMarqueeTextSecondLine(newText)));
return;
}
marqueeTextSecondLine = newText;
SplitSecondLineText(newText);
using (Graphics graphics = this.CreateGraphics())
{
float textWidth = MeasureDisplayStringWidth(graphics, marqueeTextSecondLine, new Font("微軟正黑體", 40, FontStyle.Bold));
marqueeXPosSecondLine = (int)((this.Width - textWidth) / 2);
}
if (textSegments.Count > 1)
{
segmentSwitchTimer.Start();
}
else
{
segmentSwitchTimer.Stop();
}
// 重置計時器
if (secondLineTimer != null)
{
secondLineTimer.Stop();
secondLineTimer.Dispose();
}
secondLineTimer = new System.Windows.Forms.Timer();
secondLineTimer.Interval = 100;
secondLineStartTime = DateTime.Now;
secondLineTimer.Tick += (sender, e) =>
{
if ((DateTime.Now - secondLineStartTime).TotalMilliseconds >= 30000) // 30秒
{
marqueeTextSecondLine = "";
textSegments.Clear(); // 清除分段文本
if (segmentSwitchTimer != null)
{
segmentSwitchTimer.Stop(); // 停止分段切換計時器
}
secondLineTimer.Stop();
secondLineTimer.Dispose();
this.Invalidate();
blackBackgroundPanel.Invalidate();
}
};
secondLineTimer.Start();
blackBackgroundPanel.Invalidate();
}
public void UpdateMarqueeTextThirdLine(string newText)
{
Console.WriteLine("UpdateMarqueeTextThirdLine called with text: " + newText);
if (InvokeRequired)
{
Invoke(new MethodInvoker(() => UpdateMarqueeTextThirdLine(newText)));
return;
}
marqueeTextThirdLine = newText;
marqueeXPosThirdLine = this.Width;
Console.WriteLine("Marquee text position reset to: " + marqueeXPosThirdLine);
Invalidate();
}
private void MarqueeTimer_Tick(object sender, EventArgs e)
{
marqueeXPos -= 2; // 調整移動速度
// 使用與顯示相同的字體來計算文本寬度
using (Graphics graphics = this.CreateGraphics())
{
float textWidth = MeasureDisplayStringWidth(graphics, marqueeText, new Font("微軟正黑體", 34, FontStyle.Bold));
// 當文本完全移出屏幕時重置位置
if (marqueeXPos < -textWidth)
{
marqueeXPos = this.Width;
}
}
this.Invalidate();
blackBackgroundPanel.Invalidate();
}
private float MeasureDisplayStringWidth(Graphics graphics, string text, Font font)
{
// 使用提供的字體來測量文本寬度
SizeF textSize = graphics.MeasureString(text, font);
return textSize.Width;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

11
PlayState.cs Normal file
View File

@ -0,0 +1,11 @@
namespace DualScreenDemo
{
public enum PlayState
{
Playing,
Played,
NotPlayed,
Skipped
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void ChinaSongsButton_Click(object sender, EventArgs e)
{
loveDuetButton.BackgroundImage = loveDuetNormalBackground;
talentShowButton.BackgroundImage = talentShowNormalBackground;
medleyDanceButton.BackgroundImage = medleyDanceNormalBackground;
ninetiesButton.BackgroundImage = ninetiesNormalBackground;
nostalgicSongsButton.BackgroundImage = nostalgicSongsNormalBackground;
chinaSongsButton.BackgroundImage = chinaActiveBackground;
chinaSongs = allSongs.Where(song => song.SongGenre.Contains("F1"))
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = chinaSongs;
totalPages = (int)Math.Ceiling((double)chinaSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void LoveDuetButton_Click(object sender, EventArgs e)
{
loveDuetButton.BackgroundImage = loveDuetActiveBackground;
talentShowButton.BackgroundImage = talentShowNormalBackground;
medleyDanceButton.BackgroundImage = medleyDanceNormalBackground;
ninetiesButton.BackgroundImage = ninetiesNormalBackground;
nostalgicSongsButton.BackgroundImage = nostalgicSongsNormalBackground;
chinaSongsButton.BackgroundImage = chinaNormalBackground;
loveDuetSongs = allSongs.Where(song => song.SongGenre.Contains("A1"))
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = loveDuetSongs;
totalPages = (int)Math.Ceiling((double)loveDuetSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void MedleyDanceButton_Click(object sender, EventArgs e)
{
loveDuetButton.BackgroundImage = loveDuetNormalBackground;
talentShowButton.BackgroundImage = talentShowNormalBackground;
medleyDanceButton.BackgroundImage = medleyDanceActiveBackground;
ninetiesButton.BackgroundImage = ninetiesNormalBackground;
nostalgicSongsButton.BackgroundImage = nostalgicSongsNormalBackground;
chinaSongsButton.BackgroundImage = chinaNormalBackground;
medleyDanceSongs = allSongs.Where(song => song.SongGenre.Contains("C1"))
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = medleyDanceSongs;
totalPages = (int)Math.Ceiling((double)medleyDanceSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void NinetiesButton_Click(object sender, EventArgs e)
{
loveDuetButton.BackgroundImage = loveDuetNormalBackground;
talentShowButton.BackgroundImage = talentShowNormalBackground;
medleyDanceButton.BackgroundImage = medleyDanceNormalBackground;
ninetiesButton.BackgroundImage = ninetiesActiveBackground;
nostalgicSongsButton.BackgroundImage = nostalgicSongsNormalBackground;
chinaSongsButton.BackgroundImage = chinaNormalBackground;
ninetiesSongs = allSongs.Where(song => song.SongGenre.Contains("D1"))
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = ninetiesSongs;
totalPages = (int)Math.Ceiling((double)ninetiesSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void NostalgicSongsButton_Click(object sender, EventArgs e)
{
loveDuetButton.BackgroundImage = loveDuetNormalBackground;
talentShowButton.BackgroundImage = talentShowNormalBackground;
medleyDanceButton.BackgroundImage = medleyDanceNormalBackground;
ninetiesButton.BackgroundImage = ninetiesNormalBackground;
nostalgicSongsButton.BackgroundImage = nostalgicSongsActiveBackground;
chinaSongsButton.BackgroundImage = chinaNormalBackground;
nostalgicSongs = allSongs.Where(song => song.SongGenre.Contains("E1"))
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = nostalgicSongs;
totalPages = (int)Math.Ceiling((double)nostalgicSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Linq;
using System.Collections.Generic;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void TalentShowButton_Click(object sender, EventArgs e)
{
loveDuetButton.BackgroundImage = loveDuetNormalBackground;
talentShowButton.BackgroundImage = talentShowActiveBackground;
medleyDanceButton.BackgroundImage = medleyDanceNormalBackground;
ninetiesButton.BackgroundImage = ninetiesNormalBackground;
nostalgicSongsButton.BackgroundImage = nostalgicSongsNormalBackground;
chinaSongsButton.BackgroundImage = chinaNormalBackground;
talentShowSongs = allSongs.Where(song => song.SongGenre.Contains("B1"))
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = talentShowSongs;
totalPages = (int)Math.Ceiling((double)talentShowSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void VietnameseSongsButton_Click(object sender, EventArgs e)
{
loveDuetButton.BackgroundImage = loveDuetNormalBackground;
talentShowButton.BackgroundImage = talentShowNormalBackground;
medleyDanceButton.BackgroundImage = medleyDanceNormalBackground;
ninetiesButton.BackgroundImage = ninetiesNormalBackground;
nostalgicSongsButton.BackgroundImage = nostalgicSongsNormalBackground;
chinaSongsButton.BackgroundImage = chinaNormalBackground;
vietnameseSongs = allSongs.Where(song => song.SongGenre.Contains("G1"))
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = vietnameseSongs;
totalPages = (int)Math.Ceiling((double)vietnameseSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,257 @@
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.IO;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private Button categorySearchButton;
private Bitmap categorySearchNormalBackground;
private Bitmap categorySearchActiveBackground;
private Button loveDuetButton;
private Bitmap loveDuetNormalBackground;
private Bitmap loveDuetActiveBackground;
private Button talentShowButton;
private Bitmap talentShowNormalBackground;
private Bitmap talentShowActiveBackground;
private Button medleyDanceButton;
private Bitmap medleyDanceNormalBackground;
private Bitmap medleyDanceActiveBackground;
private Button ninetiesButton;
private Bitmap ninetiesNormalBackground;
private Bitmap ninetiesActiveBackground;
private Button nostalgicSongsButton;
private Bitmap nostalgicSongsNormalBackground;
private Bitmap nostalgicSongsActiveBackground;
private Button chinaSongsButton;
private Bitmap chinaNormalBackground;
private Bitmap chinaActiveBackground;
private Button vietnameseSongsButton;
private Bitmap vietnameseNormalBackground;
private Bitmap vietnameseActiveBackground;
private void InitializeButtonsForCategorySearch()
{
categorySearchButton = new Button { Text = "" };
categorySearchButton.Name = "categorySearchButton";
ResizeAndPositionButton(categorySearchButton, 731, 97, 99, 99);
Rectangle categorySearchCropArea = new Rectangle(731, 97, 99, 99);
categorySearchNormalBackground = new Bitmap(Path.Combine(Application.StartupPath, "themes\\superstar\\ICON上方\\上方ICON_類別查詢-07.png"));
categorySearchActiveBackground = mouseDownImage.Clone(categorySearchCropArea, mouseDownImage.PixelFormat);
categorySearchButton.BackgroundImage = categorySearchNormalBackground;
categorySearchButton.BackgroundImageLayout = ImageLayout.Stretch;
categorySearchButton.FlatStyle = FlatStyle.Flat;
categorySearchButton.FlatAppearance.BorderSize = 0;
categorySearchButton.Click += CategorySearchButton_Click;
this.Controls.Add(categorySearchButton);
}
private void InitializeCategorySearchButtons()
{
loveDuetButton = new Button { Text = "", Visible = false };
ResizeAndPositionButton(loveDuetButton, 1214, 230, 209, 59);
Rectangle loveDuetButtonCropArea = new Rectangle(1214, 230, 209, 59);
loveDuetNormalBackground = normalStateImageCategoryQuery.Clone(loveDuetButtonCropArea, normalStateImageCategoryQuery.PixelFormat);
loveDuetActiveBackground = mouseDownImageCategoryQuery.Clone(loveDuetButtonCropArea, mouseDownImageCategoryQuery.PixelFormat);
loveDuetButton.BackgroundImage = loveDuetNormalBackground;
loveDuetButton.BackgroundImageLayout = ImageLayout.Stretch;
loveDuetButton.FlatStyle = FlatStyle.Flat;
loveDuetButton.FlatAppearance.BorderSize = 0;
loveDuetButton.Click += LoveDuetButton_Click;
this.Controls.Add(loveDuetButton);
talentShowButton = new Button { Text = "", Visible = false };
ResizeAndPositionButton(talentShowButton, 1214, 293, 209, 58);
Rectangle talentShowButtonCropArea = new Rectangle(1214, 293, 209, 58);
talentShowNormalBackground = normalStateImageCategoryQuery.Clone(talentShowButtonCropArea, normalStateImageCategoryQuery.PixelFormat);
talentShowActiveBackground = mouseDownImageCategoryQuery.Clone(talentShowButtonCropArea, mouseDownImageCategoryQuery.PixelFormat);
talentShowButton.BackgroundImage = talentShowNormalBackground;
talentShowButton.BackgroundImageLayout = ImageLayout.Stretch;
talentShowButton.FlatStyle = FlatStyle.Flat;
talentShowButton.FlatAppearance.BorderSize = 0;
talentShowButton.Click += TalentShowButton_Click;
this.Controls.Add(talentShowButton);
medleyDanceButton = new Button { Text = "", Visible = false };
ResizeAndPositionButton(medleyDanceButton, 1214, 356, 209, 58);
Rectangle medleyDanceButtonCropArea = new Rectangle(1214, 356, 209, 58);
medleyDanceNormalBackground = normalStateImageCategoryQuery.Clone(medleyDanceButtonCropArea, normalStateImageCategoryQuery.PixelFormat);
medleyDanceActiveBackground = mouseDownImageCategoryQuery.Clone(medleyDanceButtonCropArea, mouseDownImageCategoryQuery.PixelFormat);
medleyDanceButton.BackgroundImage = medleyDanceNormalBackground;
medleyDanceButton.BackgroundImageLayout = ImageLayout.Stretch;
medleyDanceButton.FlatStyle = FlatStyle.Flat;
medleyDanceButton.FlatAppearance.BorderSize = 0;
medleyDanceButton.Click += MedleyDanceButton_Click;
this.Controls.Add(medleyDanceButton);
ninetiesButton = new Button { Text = "", Visible = false };
ResizeAndPositionButton(ninetiesButton, 1214, 418, 209, 59);
Rectangle ninetiesButtonCropArea = new Rectangle(1214, 418, 209, 59);
ninetiesNormalBackground = normalStateImageCategoryQuery.Clone(ninetiesButtonCropArea, normalStateImageCategoryQuery.PixelFormat);
ninetiesActiveBackground = mouseDownImageCategoryQuery.Clone(ninetiesButtonCropArea, mouseDownImageCategoryQuery.PixelFormat);
ninetiesButton.BackgroundImage = ninetiesNormalBackground;
ninetiesButton.BackgroundImageLayout = ImageLayout.Stretch;
ninetiesButton.FlatStyle = FlatStyle.Flat;
ninetiesButton.FlatAppearance.BorderSize = 0;
ninetiesButton.Click += NinetiesButton_Click;
this.Controls.Add(ninetiesButton);
nostalgicSongsButton = new Button { Text = "", Visible = false };
ResizeAndPositionButton(nostalgicSongsButton, 1214, 481, 209, 59);
Rectangle nostalgicSongsButtonCropArea = new Rectangle(1214, 481, 209, 59);
nostalgicSongsNormalBackground = normalStateImageCategoryQuery.Clone(nostalgicSongsButtonCropArea, normalStateImageCategoryQuery.PixelFormat);
nostalgicSongsActiveBackground = mouseDownImageCategoryQuery.Clone(nostalgicSongsButtonCropArea, mouseDownImageCategoryQuery.PixelFormat);
nostalgicSongsButton.BackgroundImage = nostalgicSongsNormalBackground;
nostalgicSongsButton.BackgroundImageLayout = ImageLayout.Stretch;
nostalgicSongsButton.FlatStyle = FlatStyle.Flat;
nostalgicSongsButton.FlatAppearance.BorderSize = 0;
nostalgicSongsButton.Click += NostalgicSongsButton_Click;
this.Controls.Add(nostalgicSongsButton);
chinaSongsButton = new Button { Text = "", Visible = false };
ResizeAndPositionButton(chinaSongsButton, 1214, 544, 209, 58);
Rectangle chinaCropArea = new Rectangle(1214, 544, 209, 58);
chinaNormalBackground = normalStateImageCategoryQuery.Clone(chinaCropArea, normalStateImageCategoryQuery.PixelFormat);
chinaActiveBackground = mouseDownImageCategoryQuery.Clone(chinaCropArea, mouseDownImageCategoryQuery.PixelFormat);
chinaSongsButton.BackgroundImage = chinaNormalBackground;
chinaSongsButton.BackgroundImageLayout = ImageLayout.Stretch;
chinaSongsButton.FlatStyle = FlatStyle.Flat;
chinaSongsButton.FlatAppearance.BorderSize = 0;
chinaSongsButton.Click += ChinaSongsButton_Click;
this.Controls.Add(chinaSongsButton);
vietnameseSongsButton = new Button { Text = "", Visible = false };
ResizeAndPositionButton(vietnameseSongsButton, 1214, 607, 209, 58);
Rectangle vietnameseCropArea = new Rectangle(1214, 607, 209, 58);
vietnameseNormalBackground = normalStateImageCategoryQuery.Clone(vietnameseCropArea, normalStateImageCategoryQuery.PixelFormat);
vietnameseActiveBackground = mouseDownImageCategoryQuery.Clone(vietnameseCropArea, mouseDownImageCategoryQuery.PixelFormat);
vietnameseSongsButton.BackgroundImage = vietnameseNormalBackground;
vietnameseSongsButton.BackgroundImageLayout = ImageLayout.Stretch;
vietnameseSongsButton.FlatStyle = FlatStyle.Flat;
vietnameseSongsButton.FlatAppearance.BorderSize = 0;
vietnameseSongsButton.Click += VietnameseSongsButton_Click;
this.Controls.Add(vietnameseSongsButton);
}
private void CategorySearchButton_Click(object sender, EventArgs e)
{
newSongAlertButton.BackgroundImage = newSongAlertNormalBackground;
hotPlayButton.BackgroundImage = hotPlayNormalBackground;
singerSearchButton.BackgroundImage = singerSearchNormalBackground;
songSearchButton.BackgroundImage = songSearchNormalBackground;
languageSearchButton.BackgroundImage = languageSearchNormalBackground;
groupSearchButton.BackgroundImage = groupSearchNormalBackground;
categorySearchButton.BackgroundImage = categorySearchActiveBackground;
orderedSongsButton.BackgroundImage = orderedSongsNormalBackground;
myFavoritesButton.BackgroundImage = myFavoritesNormalBackground;
promotionsButton.BackgroundImage = promotionsNormalBackground;
deliciousFoodButton.BackgroundImage = deliciousFoodNormalBackground;
isOnOrderedSongsPage = false;
loveDuetButton.BackgroundImage = loveDuetActiveBackground;
talentShowButton.BackgroundImage = talentShowNormalBackground;
medleyDanceButton.BackgroundImage = medleyDanceNormalBackground;
ninetiesButton.BackgroundImage = ninetiesNormalBackground;
nostalgicSongsButton.BackgroundImage = nostalgicSongsNormalBackground;
chinaSongsButton.BackgroundImage = chinaNormalBackground;
loveDuetSongs = allSongs.Where(song => song.SongGenre.Contains("A1"))
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = loveDuetSongs;
totalPages = (int)Math.Ceiling((double)loveDuetSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
SetHotSongButtonsVisibility(false);
SetNewSongButtonsVisibility(false);
SetSingerSearchButtonsVisibility(false);
SetSongSearchButtonsVisibility(false);
SetPictureBoxLanguageButtonsVisibility(false);
SetGroupButtonsVisibility(false);
SetZhuYinSingersAndButtonsVisibility(false);
SetZhuYinSongsAndButtonsVisibility(false);
SetEnglishSingersAndButtonsVisibility(false);
SetEnglishSongsAndButtonsVisibility(false);
SetPinYinSingersAndButtonsVisibility(false);
SetPinYinSongsAndButtonsVisibility(false);
SetPictureBoxToggleLightAndButtonsVisibility(false);
SetPictureBoxSceneSoundEffectsAndButtonsVisibility(false);
SetPictureBoxCategoryAndButtonsVisibility(true);
if (pictureBoxQRCode != null)
{
pictureBoxQRCode.Visible = false;
closeQRCodeButton.Visible = false;
}
}
private void TogglePictureBoxCategoryButtonsVisibility()
{
bool areButtonsVisible = loveDuetButton.Visible;
SetPictureBoxCategoryAndButtonsVisibility(!areButtonsVisible);
}
private void SetPictureBoxCategoryAndButtonsVisibility(bool isVisible)
{
loveDuetButton.Visible = isVisible;
loveDuetButton.BringToFront();
talentShowButton.Visible = isVisible;
talentShowButton.BringToFront();
medleyDanceButton.Visible = isVisible;
medleyDanceButton.BringToFront();
ninetiesButton.Visible = isVisible;
ninetiesButton.BringToFront();
nostalgicSongsButton.Visible = isVisible;
nostalgicSongsButton.BringToFront();
chinaSongsButton.Visible = isVisible;
chinaSongsButton.BringToFront();
vietnameseSongsButton.Visible = isVisible;
vietnameseSongsButton.BringToFront();
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void GroupGuoYuButton_Click(object sender, EventArgs e)
{
groupGuoYuButton.BackgroundImage = groupGuoYuActiveBackground;
groupTaiYuButton.BackgroundImage = groupTaiYuNormalBackground;
groupYueYuButton.BackgroundImage = groupYueYuNormalBackground;
groupYingWenButton.BackgroundImage = groupYingWenNormalBackground;
groupRiYuButton.BackgroundImage = groupRiYuNormalBackground;
groupHanYuButton.BackgroundImage = groupHanYuNormalBackground;
guoYuSongs = allSongs.Where(song => song.Category == "國語" && (song.ArtistACategory == "團" || song.ArtistBCategory == "團"))
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = guoYuSongs;
totalPages = (int)Math.Ceiling((double)guoYuSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void GroupHanYuButton_Click(object sender, EventArgs e)
{
groupGuoYuButton.BackgroundImage = groupGuoYuNormalBackground;
groupTaiYuButton.BackgroundImage = groupTaiYuNormalBackground;
groupYueYuButton.BackgroundImage = groupYueYuNormalBackground;
groupYingWenButton.BackgroundImage = groupYingWenNormalBackground;
groupRiYuButton.BackgroundImage = groupRiYuNormalBackground;
groupHanYuButton.BackgroundImage = groupHanYuActiveBackground;
hanYuSongs = allSongs.Where(song => song.Category == "韓語" && (song.ArtistACategory == "團" || song.ArtistBCategory == "團"))
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = hanYuSongs;
totalPages = (int)Math.Ceiling((double)hanYuSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void GroupRiYuButton_Click(object sender, EventArgs e)
{
groupGuoYuButton.BackgroundImage = groupGuoYuNormalBackground;
groupTaiYuButton.BackgroundImage = groupTaiYuNormalBackground;
groupYueYuButton.BackgroundImage = groupYueYuNormalBackground;
groupYingWenButton.BackgroundImage = groupYingWenNormalBackground;
groupRiYuButton.BackgroundImage = groupRiYuActiveBackground;
groupHanYuButton.BackgroundImage = groupHanYuNormalBackground;
riYuSongs = allSongs.Where(song => song.Category == "日語" && (song.ArtistACategory == "團" || song.ArtistBCategory == "團"))
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = riYuSongs;
totalPages = (int)Math.Ceiling((double)riYuSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void GroupTaiYuButton_Click(object sender, EventArgs e)
{
groupGuoYuButton.BackgroundImage = groupGuoYuNormalBackground;
groupTaiYuButton.BackgroundImage = groupTaiYuActiveBackground;
groupYueYuButton.BackgroundImage = groupYueYuNormalBackground;
groupYingWenButton.BackgroundImage = groupYingWenNormalBackground;
groupRiYuButton.BackgroundImage = groupRiYuNormalBackground;
groupHanYuButton.BackgroundImage = groupHanYuNormalBackground;
taiYuSongs = allSongs.Where(song => song.Category == "台語" && (song.ArtistACategory == "團" || song.ArtistBCategory == "團"))
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = taiYuSongs;
totalPages = (int)Math.Ceiling((double)taiYuSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void GroupYingWenButton_Click(object sender, EventArgs e)
{
groupGuoYuButton.BackgroundImage = groupGuoYuNormalBackground;
groupTaiYuButton.BackgroundImage = groupTaiYuNormalBackground;
groupYueYuButton.BackgroundImage = groupYueYuNormalBackground;
groupYingWenButton.BackgroundImage = groupYingWenActiveBackground;
groupRiYuButton.BackgroundImage = groupRiYuNormalBackground;
groupHanYuButton.BackgroundImage = groupHanYuNormalBackground;
yingWenSongs = allSongs.Where(song => song.Category == "英文" && (song.ArtistACategory == "團" || song.ArtistBCategory == "團"))
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = yingWenSongs;
totalPages = (int)Math.Ceiling((double)yingWenSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void GroupYueYuButton_Click(object sender, EventArgs e)
{
groupGuoYuButton.BackgroundImage = groupGuoYuNormalBackground;
groupTaiYuButton.BackgroundImage = groupTaiYuNormalBackground;
groupYueYuButton.BackgroundImage = groupYueYuActiveBackground;
groupYingWenButton.BackgroundImage = groupYingWenNormalBackground;
groupRiYuButton.BackgroundImage = groupRiYuNormalBackground;
groupHanYuButton.BackgroundImage = groupHanYuNormalBackground;
yueYuSongs = allSongs.Where(song => song.Category == "粵語" && (song.ArtistACategory == "團" || song.ArtistBCategory == "團"))
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = yueYuSongs;
totalPages = (int)Math.Ceiling((double)yueYuSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,187 @@
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private Button groupSearchButton;
private Bitmap groupSearchNormalBackground;
private Bitmap groupSearchActiveBackground;
private Button groupGuoYuButton;
private Bitmap groupGuoYuNormalBackground;
private Bitmap groupGuoYuActiveBackground;
private Button groupTaiYuButton;
private Bitmap groupTaiYuNormalBackground;
private Bitmap groupTaiYuActiveBackground;
private Button groupYueYuButton;
private Bitmap groupYueYuNormalBackground;
private Bitmap groupYueYuActiveBackground;
private Button groupYingWenButton;
private Bitmap groupYingWenNormalBackground;
private Bitmap groupYingWenActiveBackground;
private Button groupRiYuButton;
private Bitmap groupRiYuNormalBackground;
private Bitmap groupRiYuActiveBackground;
private Button groupHanYuButton;
private Bitmap groupHanYuNormalBackground;
private Bitmap groupHanYuActiveBackground;
private void InitializeButtonsForGroupPictureBox()
{
groupGuoYuButton = new Button { Text = "", Visible = false };
ResizeAndPositionButton(groupGuoYuButton, 1214, 230, 209, 59);
Rectangle groupGuoYuButtonCropArea = new Rectangle(1214, 230, 209, 59);
groupGuoYuNormalBackground = normalStateImageLanguageQuery.Clone(groupGuoYuButtonCropArea, normalStateImageLanguageQuery.PixelFormat);
groupGuoYuActiveBackground = mouseDownImageLanguageQuery.Clone(groupGuoYuButtonCropArea, mouseDownImageLanguageQuery.PixelFormat);
groupGuoYuButton.BackgroundImage = groupGuoYuNormalBackground;
groupGuoYuButton.BackgroundImageLayout = ImageLayout.Stretch;
groupGuoYuButton.FlatStyle = FlatStyle.Flat;
groupGuoYuButton.FlatAppearance.BorderSize = 0;
groupGuoYuButton.Click += GroupGuoYuButton_Click;
this.Controls.Add(groupGuoYuButton);
groupTaiYuButton = new Button { Text = "", Visible = false };
ResizeAndPositionButton(groupTaiYuButton, 1214, 293, 209, 58);
Rectangle groupTaiYuButtonCropArea = new Rectangle(1214, 293, 209, 58);
groupTaiYuNormalBackground = normalStateImageLanguageQuery.Clone(groupTaiYuButtonCropArea, normalStateImageLanguageQuery.PixelFormat);
groupTaiYuActiveBackground = mouseDownImageLanguageQuery.Clone(groupTaiYuButtonCropArea, mouseDownImageLanguageQuery.PixelFormat);
groupTaiYuButton.BackgroundImage = groupTaiYuNormalBackground;
groupTaiYuButton.BackgroundImageLayout = ImageLayout.Stretch;
groupTaiYuButton.FlatStyle = FlatStyle.Flat;
groupTaiYuButton.FlatAppearance.BorderSize = 0;
groupTaiYuButton.Click += GroupTaiYuButton_Click;
this.Controls.Add(groupTaiYuButton);
groupYueYuButton = new Button { Text = "", Visible = false };
ResizeAndPositionButton(groupYueYuButton, 1214, 356, 209, 58);
Rectangle groupYueYuButtonCropArea = new Rectangle(1214, 356, 209, 58);
groupYueYuNormalBackground = normalStateImageLanguageQuery.Clone(groupYueYuButtonCropArea, normalStateImageLanguageQuery.PixelFormat);
groupYueYuActiveBackground = mouseDownImageLanguageQuery.Clone(groupYueYuButtonCropArea, mouseDownImageLanguageQuery.PixelFormat);
groupYueYuButton.BackgroundImage = groupYueYuNormalBackground;
groupYueYuButton.BackgroundImageLayout = ImageLayout.Stretch;
groupYueYuButton.FlatStyle = FlatStyle.Flat;
groupYueYuButton.FlatAppearance.BorderSize = 0;
groupYueYuButton.Click += GroupYueYuButton_Click;
this.Controls.Add(groupYueYuButton);
groupYingWenButton = new Button { Text = "", Visible = false };
ResizeAndPositionButton(groupYingWenButton, 1214, 418, 209, 59);
Rectangle groupYingWenButtonCropArea = new Rectangle(1214, 418, 209, 59);
groupYingWenNormalBackground = normalStateImageLanguageQuery.Clone(groupYingWenButtonCropArea, normalStateImageLanguageQuery.PixelFormat);
groupYingWenActiveBackground = mouseDownImageLanguageQuery.Clone(groupYingWenButtonCropArea, mouseDownImageLanguageQuery.PixelFormat);
groupYingWenButton.BackgroundImage = groupYingWenNormalBackground;
groupYingWenButton.BackgroundImageLayout = ImageLayout.Stretch;
groupYingWenButton.FlatStyle = FlatStyle.Flat;
groupYingWenButton.FlatAppearance.BorderSize = 0;
groupYingWenButton.Click += GroupYingWenButton_Click;
this.Controls.Add(groupYingWenButton);
groupRiYuButton = new Button { Text = "", Visible = false };
ResizeAndPositionButton(groupRiYuButton, 1214, 481, 209, 59);
Rectangle groupRiYuButtonCropArea = new Rectangle(1214, 481, 209, 59);
groupRiYuNormalBackground = normalStateImageLanguageQuery.Clone(groupRiYuButtonCropArea, normalStateImageLanguageQuery.PixelFormat);
groupRiYuActiveBackground = mouseDownImageLanguageQuery.Clone(groupRiYuButtonCropArea, mouseDownImageLanguageQuery.PixelFormat);
groupRiYuButton.BackgroundImage = groupRiYuNormalBackground;
groupRiYuButton.BackgroundImageLayout = ImageLayout.Stretch;
groupRiYuButton.FlatStyle = FlatStyle.Flat;
groupRiYuButton.FlatAppearance.BorderSize = 0;
groupRiYuButton.Click += GroupRiYuButton_Click;
this.Controls.Add(groupRiYuButton);
groupHanYuButton = new Button { Text = "", Visible = false };
ResizeAndPositionButton(groupHanYuButton, 1214, 544, 209, 58);
Rectangle groupHanYuButtonCropArea = new Rectangle(1214, 544, 209, 58);
groupHanYuNormalBackground = normalStateImageLanguageQuery.Clone(groupHanYuButtonCropArea, normalStateImageLanguageQuery.PixelFormat);
groupHanYuActiveBackground = mouseDownImageLanguageQuery.Clone(groupHanYuButtonCropArea, mouseDownImageLanguageQuery.PixelFormat);
groupHanYuButton.BackgroundImage = groupHanYuNormalBackground;
groupHanYuButton.BackgroundImageLayout = ImageLayout.Stretch;
groupHanYuButton.FlatStyle = FlatStyle.Flat;
groupHanYuButton.FlatAppearance.BorderSize = 0;
groupHanYuButton.Click += GroupHanYuButton_Click;
this.Controls.Add(groupHanYuButton);
}
private void GroupSongSelectionButton_Click(object sender, EventArgs e)
{
newSongAlertButton.BackgroundImage = newSongAlertNormalBackground;
hotPlayButton.BackgroundImage = hotPlayNormalBackground;
singerSearchButton.BackgroundImage = singerSearchNormalBackground;
songSearchButton.BackgroundImage = songSearchNormalBackground;
languageSearchButton.BackgroundImage = languageSearchNormalBackground;
groupSearchButton.BackgroundImage = groupSearchActiveBackground;
categorySearchButton.BackgroundImage = categorySearchNormalBackground;
orderedSongsButton.BackgroundImage = orderedSongsNormalBackground;
myFavoritesButton.BackgroundImage = myFavoritesNormalBackground;
promotionsButton.BackgroundImage = promotionsNormalBackground;
deliciousFoodButton.BackgroundImage = deliciousFoodNormalBackground;
isOnOrderedSongsPage = false;
groupGuoYuButton.BackgroundImage = groupGuoYuActiveBackground;
groupTaiYuButton.BackgroundImage = groupTaiYuNormalBackground;
groupYueYuButton.BackgroundImage = groupYueYuNormalBackground;
groupYingWenButton.BackgroundImage = groupYingWenNormalBackground;
groupRiYuButton.BackgroundImage = groupRiYuNormalBackground;
groupHanYuButton.BackgroundImage = groupHanYuNormalBackground;
guoYuSongs = allSongs.Where(song => song.Category == "國語" && (song.ArtistACategory == "團" || song.ArtistBCategory == "團"))
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = guoYuSongs;
totalPages = (int)Math.Ceiling((double)guoYuSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
SetHotSongButtonsVisibility(false);
SetNewSongButtonsVisibility(false);
SetSingerSearchButtonsVisibility(false);
SetSongSearchButtonsVisibility(false);
SetPictureBoxLanguageButtonsVisibility(false);
SetPictureBoxCategoryAndButtonsVisibility(false);
SetZhuYinSingersAndButtonsVisibility(false);
SetZhuYinSongsAndButtonsVisibility(false);
SetEnglishSingersAndButtonsVisibility(false);
SetEnglishSongsAndButtonsVisibility(false);
SetPinYinSingersAndButtonsVisibility(false);
SetPinYinSongsAndButtonsVisibility(false);
SetPictureBoxToggleLightAndButtonsVisibility(false);
SetPictureBoxSceneSoundEffectsAndButtonsVisibility(false);
SetGroupButtonsVisibility(true);
if (pictureBoxQRCode != null)
{
pictureBoxQRCode.Visible = false;
closeQRCodeButton.Visible = false;
}
}
private void SetGroupButtonsVisibility(bool isVisible)
{
Button[] pictureBox6Buttons = { groupGuoYuButton, groupTaiYuButton, groupYueYuButton, groupYingWenButton, groupRiYuButton, groupHanYuButton };
foreach (var button in pictureBox6Buttons)
{
button.Visible = isVisible;
if (isVisible)
{
button.BringToFront();
}
}
}
}
}

View File

@ -0,0 +1,288 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using DBObj;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private Button hotPlayButton;
private Bitmap hotPlayNormalBackground;
private Bitmap hotPlayActiveBackground;
private Button guoYuButtonHotSong;
private Bitmap guoYuHotSongNormalBackground;
private Bitmap guoYuHotSongActiveBackground;
private Button taiYuButtonHotSong;
private Bitmap taiYuHotSongNormalBackground;
private Bitmap taiYuHotSongActiveBackground;
private Button taiYuNewSongButtonHotSong;
private Bitmap taiYuNewSongHotSongNormalBackground;
private Bitmap taiYuNewSongHotSongActiveBackground;
private Button guoYuNewSongButtonHotSong;
private Bitmap guoYuNewSongHotSongNormalBackground;
private Bitmap guoYuNewSongHotSongActiveBackground;
private Button yueYuButtonHotSong;
private Bitmap yueYuHotSongNormalBackground;
private Bitmap yueYuHotSongActiveBackground;
private Button yingWenButtonHotSong;
private Bitmap yingWenHotSongNormalBackground;
private Bitmap yingWenHotSongActiveBackground;
private Button riYuButtonHotSong;
private Bitmap riYuHotSongNormalBackground;
private Bitmap riYuHotSongActiveBackground;
private Button hanYuButtonHotSong;
private Bitmap hanYuHotSongNormalBackground;
private Bitmap hanYuHotSongActiveBackground;
private void SetHotSongButtonsVisibility(bool isVisible)
{
Button[] hotSongButtons = { guoYuButtonHotSong, taiYuButtonHotSong, taiYuNewSongButtonHotSong, guoYuNewSongButtonHotSong, yingWenButtonHotSong, riYuButtonHotSong, hanYuButtonHotSong };
foreach (var button in hotSongButtons)
{
button.Visible = isVisible;
if (isVisible)
{
button.BringToFront();
}
}
}
private void HotPlayButton_Click(object sender, EventArgs e)
{
UpdateButtonBackgrounds(hotPlayButton, hotPlayActiveBackground);
UpdateHotSongButtons(guoYuButtonHotSong, guoYuHotSongActiveBackground);
isOnOrderedSongsPage = false;
int songLimit = ReadHotSongLimit();
guoYuSongs = GetSongsByCategory("國語", songLimit);
UpdateSongList(guoYuSongs);
SetButtonsVisibility();
HideQRCode();
}
private void UpdateButtonBackgrounds(Button activeButton, Image activeBackground)
{
newSongAlertButton.BackgroundImage = newSongAlertNormalBackground;
hotPlayButton.BackgroundImage = hotPlayNormalBackground;
singerSearchButton.BackgroundImage = singerSearchNormalBackground;
songSearchButton.BackgroundImage = songSearchNormalBackground;
languageSearchButton.BackgroundImage = languageSearchNormalBackground;
groupSearchButton.BackgroundImage = groupSearchNormalBackground;
categorySearchButton.BackgroundImage = categorySearchNormalBackground;
orderedSongsButton.BackgroundImage = orderedSongsNormalBackground;
myFavoritesButton.BackgroundImage = myFavoritesNormalBackground;
promotionsButton.BackgroundImage = promotionsNormalBackground;
deliciousFoodButton.BackgroundImage = deliciousFoodNormalBackground;
activeButton.BackgroundImage = activeBackground;
}
private void OnHotSongButtonClick(Button activeButton, Bitmap activeBackground, string category)
{
UpdateHotSongButtons(activeButton, activeBackground);
int songLimit = ReadHotSongLimit();
var selectedSongs = allSongs.Where(song => song.Category == category)
.OrderByDescending(song => song.Plays)
.Take(songLimit)
.ToList();
UpdateSongList(selectedSongs);
}
private void UpdateHotSongButtons(Button activeButton, Image activeBackground)
{
guoYuButtonHotSong.BackgroundImage = guoYuHotSongNormalBackground;
taiYuButtonHotSong.BackgroundImage = taiYuHotSongNormalBackground;
taiYuNewSongButtonHotSong.BackgroundImage = taiYuNewSongHotSongNormalBackground;
guoYuNewSongButtonHotSong.BackgroundImage = guoYuNewSongHotSongNormalBackground;
yingWenButtonHotSong.BackgroundImage = yingWenHotSongNormalBackground;
riYuButtonHotSong.BackgroundImage = riYuHotSongNormalBackground;
hanYuButtonHotSong.BackgroundImage = hanYuHotSongNormalBackground;
activeButton.BackgroundImage = activeBackground;
}
private List<SongData> GetSongsByCategory(string category, int limit)
{
return allSongs.Where(song => song.Category == category)
.OrderByDescending(song => song.Plays)
.Take(limit)
.ToList();
}
private void UpdateSongList(List<SongData> songs)
{
currentPage = 0;
currentSongList = songs;
totalPages = (int)Math.Ceiling((double)songs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
private void SetButtonsVisibility()
{
SetNewSongButtonsVisibility(false);
SetSingerSearchButtonsVisibility(false);
SetSongSearchButtonsVisibility(false);
SetPictureBoxLanguageButtonsVisibility(false);
SetGroupButtonsVisibility(false);
SetPictureBoxCategoryAndButtonsVisibility(false);
SetZhuYinSingersAndButtonsVisibility(false);
SetZhuYinSongsAndButtonsVisibility(false);
SetEnglishSingersAndButtonsVisibility(false);
SetEnglishSongsAndButtonsVisibility(false);
SetPinYinSingersAndButtonsVisibility(false);
SetPinYinSongsAndButtonsVisibility(false);
SetPictureBoxToggleLightAndButtonsVisibility(false);
SetPictureBoxSceneSoundEffectsAndButtonsVisibility(false);
SetHotSongButtonsVisibility(true);
}
private void HideQRCode()
{
if (pictureBoxQRCode != null)
{
pictureBoxQRCode.Visible = false;
closeQRCodeButton.Visible = false;
}
}
private void InitializeButtonsForHotSong()
{
InitializeHotSongButton(ref guoYuNewSongButtonHotSong, "國語新歌", 1214, 230, 209, 58,
normalStateImageHotSong,
out guoYuNewSongHotSongNormalBackground,
mouseDownImageHotSong,
out guoYuNewSongHotSongActiveBackground,
GuoYuNewSongButtonHotSong_Click);
InitializeHotSongButton(ref taiYuNewSongButtonHotSong, "台語新歌", 1214, 293, 209, 58,
normalStateImageHotSong,
out taiYuNewSongHotSongNormalBackground,
mouseDownImageHotSong,
out taiYuNewSongHotSongActiveBackground,
TaiYuNewSongButtonHotSong_Click);
InitializeHotSongButton(ref taiYuButtonHotSong, "台語", 1214, 418, 209, 58,
normalStateImageHotSong,
out taiYuHotSongNormalBackground,
mouseDownImageHotSong,
out taiYuHotSongActiveBackground,
TaiYuButtonHotSong_Click);
InitializeHotSongButton(ref yueYuButtonHotSong, "粵語", 1214, 356, 209, 58,
normalStateImageHotSong,
out yueYuHotSongNormalBackground,
mouseDownImageHotSong,
out yueYuHotSongActiveBackground,
YueYuButtonHotSong_Click);
InitializeHotSongButton(ref guoYuButtonHotSong, "國語", 1214, 356, 209, 59,
normalStateImageHotSong,
out guoYuHotSongNormalBackground,
mouseDownImageHotSong,
out guoYuHotSongActiveBackground,
GuoYuButtonHotSong_Click);
InitializeHotSongButton(ref yingWenButtonHotSong, "英文", 1214, 481, 209, 59,
normalStateImageHotSong,
out yingWenHotSongNormalBackground,
mouseDownImageHotSong,
out yingWenHotSongActiveBackground,
YingWenButtonHotSong_Click);
InitializeHotSongButton(ref riYuButtonHotSong, "日語", 1214, 544, 209, 59,
normalStateImageHotSong,
out riYuHotSongNormalBackground,
mouseDownImageHotSong,
out riYuHotSongActiveBackground,
RiYuButtonHotSong_Click);
InitializeHotSongButton(ref hanYuButtonHotSong, "韓語", 1214, 607, 209, 58,
normalStateImageHotSong,
out hanYuHotSongNormalBackground,
mouseDownImageHotSong,
out hanYuHotSongActiveBackground,
HanYuButtonHotSong_Click);
}
private void InitializeHotSongButton(ref Button button, string buttonText, int x, int y, int width, int height,
Image normalBackground, out Bitmap normalBackgroundOut,
Image activeBackground, out Bitmap activeBackgroundOut,
EventHandler clickEventHandler)
{
button = new Button {
Text = "", // 移除文字
Visible = false
};
ResizeAndPositionButton(button, x, y, width, height);
// 修改裁剪區域,避開文字部分
Rectangle cropArea = new Rectangle(1214, y, 209, 58); // 使用固定的裁剪區域
normalBackgroundOut = new Bitmap(normalBackground).Clone(cropArea, normalBackground.PixelFormat);
activeBackgroundOut = new Bitmap(activeBackground).Clone(cropArea, activeBackground.PixelFormat);
button.BackgroundImage = normalBackgroundOut;
button.BackgroundImageLayout = ImageLayout.Stretch;
button.FlatStyle = FlatStyle.Flat;
button.FlatAppearance.BorderSize = 0;
button.Click += clickEventHandler;
this.Controls.Add(button);
}
public static int ReadHotSongLimit()
{
string filePath = Path.Combine(Application.StartupPath, "SongLimitsSettings.txt");
try
{
var lines = File.ReadAllLines(filePath);
foreach (var line in lines)
{
if (line.StartsWith("HotSongLimit:"))
{
string valuePart = line.Split(':')[1].Trim();
int limit;
if (int.TryParse(valuePart, out limit))
{
return limit;
}
break;
}
}
}
catch (Exception ex)
{
Console.WriteLine("Failed to read song limits from file: " + ex.Message);
return 100;
}
return 100;
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void YueYuButtonHotSong_Click(object sender, EventArgs e)
{
OnHotSongButtonClick(yueYuButtonHotSong, yueYuHotSongActiveBackground, "粵語");
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void GuoYuButtonHotSong_Click(object sender, EventArgs e)
{
OnHotSongButtonClick(guoYuButtonHotSong, guoYuHotSongActiveBackground, "國語");
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void YingWenButtonHotSong_Click(object sender, EventArgs e)
{
OnHotSongButtonClick(yingWenButtonHotSong, yingWenHotSongActiveBackground, "英文");
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void RiYuButtonHotSong_Click(object sender, EventArgs e)
{
OnHotSongButtonClick(riYuButtonHotSong, riYuHotSongActiveBackground, "日語");
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void HanYuButtonHotSong_Click(object sender, EventArgs e)
{
OnHotSongButtonClick(hanYuButtonHotSong, hanYuHotSongActiveBackground, "韓語");
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void GuoYuNewSongButtonHotSong_Click(object sender, EventArgs e)
{
// 重置其他按钮背景
UpdateHotSongButtons(guoYuNewSongButtonHotSong, guoYuNewSongHotSongActiveBackground);
int songLimit = ReadHotSongLimit();
// 使用 AddedTime 排序
var selectedSongs = allSongs.Where(song => song.Category == "國語")
.OrderByDescending(song => song.AddedTime)
.Take(songLimit)
.ToList();
currentPage = 0;
currentSongList = selectedSongs;
totalPages = (int)Math.Ceiling((double)selectedSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void TaiYuButtonHotSong_Click(object sender, EventArgs e)
{
OnHotSongButtonClick(taiYuButtonHotSong, taiYuHotSongActiveBackground, "台語");
}
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void TaiYuNewSongButtonHotSong_Click(object sender, EventArgs e)
{
// 重置其他按钮背景
UpdateHotSongButtons(taiYuNewSongButtonHotSong, taiYuNewSongHotSongActiveBackground);
int songLimit = ReadHotSongLimit();
// 使用 AddedTime 排序
var selectedSongs = allSongs.Where(song => song.Category == "台語")
.OrderByDescending(song => song.AddedTime)
.Take(songLimit)
.ToList();
currentPage = 0;
currentSongList = selectedSongs;
totalPages = (int)Math.Ceiling((double)selectedSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void InitializeGuoYuButton()
{
Rectangle guoYuButtonCropArea = new Rectangle(1214, 230, 209, 59);
InitializeButton(ref guoYuButton, "國語", 1214, 230, 209, 59, guoYuButtonCropArea, normalStateImageLanguageQuery, out guoYuNormalBackground, mouseDownImageLanguageQuery, out guoYuActiveBackground, GuoYuButton_Click);
}
private void GuoYuButton_Click(object sender, EventArgs e)
{
OnLanguageButtonClick(guoYuButton, guoYuActiveBackground, "國語");
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void InitializeHanYuButton()
{
Rectangle hanYuButtonCropArea = new Rectangle(1214, 544, 209, 58);
InitializeButton(ref hanYuButton, "韓語", 1214, 544, 209, 58, hanYuButtonCropArea, normalStateImageLanguageQuery, out hanYuNormalBackground, mouseDownImageLanguageQuery, out hanYuActiveBackground, HanYuButton_Click);
}
private void HanYuButton_Click(object sender, EventArgs e)
{
OnLanguageButtonClick(hanYuButton, hanYuActiveBackground, "韓語");
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void InitializeKeYuButton()
{
Rectangle keYuButtonCropArea = new Rectangle(1214, 607, 209, 58);
InitializeButton(ref keYuButton, "客語", 1214, 607, 209, 58, keYuButtonCropArea, normalStateImageLanguageQuery, out keYuNormalBackground, mouseDownImageLanguageQuery, out keYuActiveBackground, KeYuButton_Click);
}
private void KeYuButton_Click(object sender, EventArgs e)
{
OnLanguageButtonClick(keYuButton, keYuActiveBackground, "客語");
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void InitializeRiYuButton()
{
Rectangle riYuButtonCropArea = new Rectangle(1214, 481, 209, 59);
InitializeButton(ref riYuButton, "日語", 1214, 481, 209, 59, riYuButtonCropArea, normalStateImageLanguageQuery, out riYuNormalBackground, mouseDownImageLanguageQuery, out riYuActiveBackground, RiYuButton_Click);
}
private void RiYuButton_Click(object sender, EventArgs e)
{
OnLanguageButtonClick(riYuButton, riYuActiveBackground, "日語");
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void InitializeTaiYuButton()
{
Rectangle taiYuButtonCropArea = new Rectangle(1214, 293, 209, 58);
InitializeButton(ref taiYuButton, "台語", 1214, 293, 209, 58, taiYuButtonCropArea, normalStateImageLanguageQuery, out taiYuNormalBackground, mouseDownImageLanguageQuery, out taiYuActiveBackground, TaiYuButton_Click);
}
private void TaiYuButton_Click(object sender, EventArgs e)
{
OnLanguageButtonClick(taiYuButton, taiYuActiveBackground, "台語");
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void InitializeYingWenButton()
{
Rectangle yingWenButtonCropArea = new Rectangle(1214, 418, 209, 59);
InitializeButton(ref yingWenButton, "英文", 1214, 418, 209, 59, yingWenButtonCropArea, normalStateImageLanguageQuery, out yingWenNormalBackground, mouseDownImageLanguageQuery, out yingWenActiveBackground, YingWenButton_Click);
}
private void YingWenButton_Click(object sender, EventArgs e)
{
OnLanguageButtonClick(yingWenButton, yingWenActiveBackground, "英文");
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void InitializeYueYuButton()
{
Rectangle yueYuButtonCropArea = new Rectangle(1214, 356, 209, 58);
InitializeButton(ref yueYuButton, "粵語", 1214, 356, 209, 58, yueYuButtonCropArea, normalStateImageLanguageQuery, out yueYuNormalBackground, mouseDownImageLanguageQuery, out yueYuActiveBackground, YueYuButton_Click);
}
private void YueYuButton_Click(object sender, EventArgs e)
{
OnLanguageButtonClick(yueYuButton, yueYuActiveBackground, "粵語");
}
}
}

View File

@ -0,0 +1,173 @@
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.IO;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void InitializeButton(ref Button button, string buttonText, int x, int y, int width, int height, Rectangle cropArea, Image normalBackground, out Bitmap normalBackgroundOut, Image activeBackground, out Bitmap activeBackgroundOut, EventHandler clickEventHandler)
{
button = new Button { Text = buttonText, Visible = false };
ResizeAndPositionButton(button, x, y, width, height);
normalBackgroundOut = new Bitmap(normalBackground).Clone(cropArea, normalBackground.PixelFormat);
activeBackgroundOut = new Bitmap(activeBackground).Clone(cropArea, activeBackground.PixelFormat);
button.BackgroundImage = normalBackgroundOut;
button.BackgroundImageLayout = ImageLayout.Stretch;
button.FlatStyle = FlatStyle.Flat;
button.FlatAppearance.BorderSize = 0;
button.Click += clickEventHandler;
this.Controls.Add(button);
}
private Button languageSearchButton;
private Bitmap languageSearchNormalBackground;
private Bitmap languageSearchActiveBackground;
private Button guoYuButton;
private Bitmap guoYuNormalBackground;
private Bitmap guoYuActiveBackground;
private Button taiYuButton;
private Bitmap taiYuNormalBackground;
private Bitmap taiYuActiveBackground;
private Button yueYuButton;
private Bitmap yueYuNormalBackground;
private Bitmap yueYuActiveBackground;
private Button yingWenButton;
private Bitmap yingWenNormalBackground;
private Bitmap yingWenActiveBackground;
private Button riYuButton;
private Bitmap riYuNormalBackground;
private Bitmap riYuActiveBackground;
private Button hanYuButton;
private Bitmap hanYuNormalBackground;
private Bitmap hanYuActiveBackground;
private Button keYuButton;
private Bitmap keYuNormalBackground;
private Bitmap keYuActiveBackground;
private void LanguageSongSelectionButton_Click(object sender, EventArgs e)
{
newSongAlertButton.BackgroundImage = newSongAlertNormalBackground;
hotPlayButton.BackgroundImage = hotPlayNormalBackground;
singerSearchButton.BackgroundImage = singerSearchNormalBackground;
songSearchButton.BackgroundImage = songSearchNormalBackground;
languageSearchButton.BackgroundImage = languageSearchActiveBackground;
groupSearchButton.BackgroundImage = groupSearchNormalBackground;
categorySearchButton.BackgroundImage = categorySearchNormalBackground;
orderedSongsButton.BackgroundImage = orderedSongsNormalBackground;
myFavoritesButton.BackgroundImage = myFavoritesNormalBackground;
promotionsButton.BackgroundImage = promotionsNormalBackground;
deliciousFoodButton.BackgroundImage = deliciousFoodNormalBackground;
isOnOrderedSongsPage = false;
guoYuButton.BackgroundImage = guoYuActiveBackground;
taiYuButton.BackgroundImage = taiYuNormalBackground;
yueYuButton.BackgroundImage = yueYuNormalBackground;
yingWenButton.BackgroundImage = yingWenNormalBackground;
riYuButton.BackgroundImage = riYuNormalBackground;
hanYuButton.BackgroundImage = hanYuNormalBackground;
keYuButton.BackgroundImage = keYuNormalBackground;
guoYuSongs = allSongs.Where(song => song.Category == "國語")
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = guoYuSongs;
totalPages = (int)Math.Ceiling((double)guoYuSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
SetHotSongButtonsVisibility(false);
SetNewSongButtonsVisibility(false);
SetSingerSearchButtonsVisibility(false);
SetSongSearchButtonsVisibility(false);
SetGroupButtonsVisibility(false);
SetPictureBoxCategoryAndButtonsVisibility(false);
SetZhuYinSingersAndButtonsVisibility(false);
SetZhuYinSongsAndButtonsVisibility(false);
SetEnglishSingersAndButtonsVisibility(false);
SetEnglishSongsAndButtonsVisibility(false);
SetPinYinSingersAndButtonsVisibility(false);
SetPinYinSongsAndButtonsVisibility(false);
SetPictureBoxToggleLightAndButtonsVisibility(false);
SetPictureBoxSceneSoundEffectsAndButtonsVisibility(false);
SetPictureBoxLanguageButtonsVisibility(true);
if (pictureBoxQRCode != null)
{
pictureBoxQRCode.Visible = false;
closeQRCodeButton.Visible = false;
}
}
private void OnLanguageButtonClick(Button activeButton, Image activeBackground, string category)
{
guoYuButton.BackgroundImage = guoYuNormalBackground;
taiYuButton.BackgroundImage = taiYuNormalBackground;
yueYuButton.BackgroundImage = yueYuNormalBackground;
yingWenButton.BackgroundImage = yingWenNormalBackground;
riYuButton.BackgroundImage = riYuNormalBackground;
hanYuButton.BackgroundImage = hanYuNormalBackground;
keYuButton.BackgroundImage = keYuNormalBackground;
activeButton.BackgroundImage = activeBackground;
var selectedSongs = allSongs.Where(song => song.Category == category)
.OrderByDescending(song => song.Plays)
.ToList();
currentPage = 0;
currentSongList = selectedSongs;
totalPages = (int)Math.Ceiling((double)selectedSongs.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
private void SetPictureBoxLanguageButtonsVisibility(bool isVisible)
{
guoYuButton.Visible = isVisible;
guoYuButton.BringToFront();
taiYuButton.Visible = isVisible;
taiYuButton.BringToFront();
yueYuButton.Visible = isVisible;
yueYuButton.BringToFront();
yingWenButton.Visible = isVisible;
yingWenButton.BringToFront();
riYuButton.Visible = isVisible;
riYuButton.BringToFront();
hanYuButton.Visible = isVisible;
hanYuButton.BringToFront();
keYuButton.Visible = isVisible;
keYuButton.BringToFront();
}
private void InitializeButtonsForPictureBoxLanguageQuery()
{
InitializeGuoYuButton();
InitializeTaiYuButton();
InitializeYueYuButton();
InitializeYingWenButton();
InitializeRiYuButton();
InitializeHanYuButton();
InitializeKeYuButton();
}
}
}

View File

@ -0,0 +1,258 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private Button newSongAlertButton;
private Bitmap newSongAlertNormalBackground;
private Bitmap newSongAlertActiveBackground;
private Button guoYuButtonNewSong;
private Bitmap guoYuNewSongNormalBackground;
private Bitmap guoYuNewSongActiveBackground;
private Button taiYuButtonNewSong;
private Bitmap taiYuNewSongNormalBackground;
private Bitmap taiYuNewSongActiveBackground;
private Button yueYuButtonNewSong;
private Bitmap yueYuNewSongNormalBackground;
private Bitmap yueYuNewSongActiveBackground;
private Button yingWenButtonNewSong;
private Bitmap yingWenNewSongNormalBackground;
private Bitmap yingWenNewSongActiveBackground;
private Button riYuButtonNewSong;
private Bitmap riYuNewSongNormalBackground;
private Bitmap riYuNewSongActiveBackground;
private Button hanYuButtonNewSong;
private Bitmap hanYuNewSongNormalBackground;
private Bitmap hanYuNewSongActiveBackground;
private void ToggleNewSongButtonsVisibility()
{
bool areButtonsVisible = guoYuButtonNewSong.Visible;
SetNewSongButtonsVisibility(!areButtonsVisible);
}
private void SetNewSongButtonsVisibility(bool isVisible)
{
Button[] pictureBox2Buttons = { guoYuButtonNewSong, taiYuButtonNewSong, yueYuButtonNewSong, yingWenButtonNewSong, riYuButtonNewSong, hanYuButtonNewSong };
foreach (var button in pictureBox2Buttons)
{
button.Visible = isVisible;
if (isVisible)
{
button.BringToFront();
}
}
}
private void NewSongAlertButton_Click(object sender, EventArgs e)
{
newSongAlertButton.BackgroundImage = newSongAlertActiveBackground;
hotPlayButton.BackgroundImage = hotPlayNormalBackground;
singerSearchButton.BackgroundImage = singerSearchNormalBackground;
songSearchButton.BackgroundImage = songSearchNormalBackground;
languageSearchButton.BackgroundImage = languageSearchNormalBackground;
groupSearchButton.BackgroundImage = groupSearchNormalBackground;
categorySearchButton.BackgroundImage = categorySearchNormalBackground;
orderedSongsButton.BackgroundImage = orderedSongsNormalBackground;
myFavoritesButton.BackgroundImage = myFavoritesNormalBackground;
promotionsButton.BackgroundImage = promotionsNormalBackground;
deliciousFoodButton.BackgroundImage = deliciousFoodNormalBackground;
isOnOrderedSongsPage = false;
guoYuButtonNewSong.BackgroundImage = guoYuNewSongActiveBackground;
taiYuButtonNewSong.BackgroundImage = taiYuNewSongNormalBackground;
yueYuButtonNewSong.BackgroundImage = yueYuNewSongNormalBackground;
yingWenButtonNewSong.BackgroundImage = yingWenNewSongNormalBackground;
riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground;
hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground;
int songLimit = ReadNewSongLimit();
guoYuSongs2 = allSongs.Where(song => song.Category == "國語")
.OrderByDescending(song => song.AddedTime)
.Take(songLimit)
.ToList();
currentPage = 0;
currentSongList = guoYuSongs2;
totalPages = (int)Math.Ceiling((double)guoYuSongs2.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
SetHotSongButtonsVisibility(false);
SetSingerSearchButtonsVisibility(false);
SetSongSearchButtonsVisibility(false);
SetPictureBoxLanguageButtonsVisibility(false);
SetGroupButtonsVisibility(false);
SetPictureBoxCategoryAndButtonsVisibility(false);
SetZhuYinSingersAndButtonsVisibility(false);
SetZhuYinSongsAndButtonsVisibility(false);
SetEnglishSingersAndButtonsVisibility(false);
SetEnglishSongsAndButtonsVisibility(false);
SetPinYinSingersAndButtonsVisibility(false);
SetPinYinSongsAndButtonsVisibility(false);
SetPictureBoxToggleLightAndButtonsVisibility(false);
SetPictureBoxSceneSoundEffectsAndButtonsVisibility(false);
ToggleNewSongButtonsVisibility();
if (pictureBoxQRCode != null)
{
pictureBoxQRCode.Visible = false;
closeQRCodeButton.Visible = false;
}
}
private void InitializeButtonsForNewSong()
{
guoYuButtonNewSong = new Button{ Text = "", Visible = false };
ResizeAndPositionButton(guoYuButtonNewSong, 1214, 230, 209, 59);
Rectangle guoYuNewSongButtonCropArea = new Rectangle(1214, 230, 209, 59);
guoYuNewSongNormalBackground = normalStateImageNewSongAlert.Clone(guoYuNewSongButtonCropArea, normalStateImageNewSongAlert.PixelFormat);
guoYuNewSongActiveBackground = mouseDownImageNewSongAlert.Clone(guoYuNewSongButtonCropArea, mouseDownImageNewSongAlert.PixelFormat);
guoYuButtonNewSong.BackgroundImage = guoYuNewSongNormalBackground;
guoYuButtonNewSong.BackgroundImageLayout = ImageLayout.Stretch;
guoYuButtonNewSong.FlatStyle = FlatStyle.Flat;
guoYuButtonNewSong.FlatAppearance.BorderSize = 0;
guoYuButtonNewSong.Click += GuoYuButtonNewSong_Click;
this.Controls.Add(guoYuButtonNewSong);
taiYuButtonNewSong = new Button { Text = "", Visible = false };
ResizeAndPositionButton(taiYuButtonNewSong, 1214, 293, 209, 58);
Rectangle taiYuNewSongButtonCropArea = new Rectangle(1214, 293, 209, 58);
taiYuNewSongNormalBackground = normalStateImageNewSongAlert.Clone(taiYuNewSongButtonCropArea, normalStateImageNewSongAlert.PixelFormat);
taiYuNewSongActiveBackground = mouseDownImageNewSongAlert.Clone(taiYuNewSongButtonCropArea, mouseDownImageNewSongAlert.PixelFormat);
taiYuButtonNewSong.BackgroundImage = taiYuNewSongNormalBackground;
taiYuButtonNewSong.BackgroundImageLayout = ImageLayout.Stretch;
taiYuButtonNewSong.FlatStyle = FlatStyle.Flat;
taiYuButtonNewSong.FlatAppearance.BorderSize = 0;
taiYuButtonNewSong.Click += TaiYuButtonNewSong_Click;
this.Controls.Add(taiYuButtonNewSong);
yueYuButtonNewSong = new Button { Text = "", Visible = false };
ResizeAndPositionButton(yueYuButtonNewSong, 1214, 356, 209, 58);
Rectangle yueYuNewSongButtonCropArea = new Rectangle(1214, 356, 209, 58);
yueYuNewSongNormalBackground = normalStateImageNewSongAlert.Clone(yueYuNewSongButtonCropArea, normalStateImageNewSongAlert.PixelFormat);
yueYuNewSongActiveBackground = mouseDownImageNewSongAlert.Clone(yueYuNewSongButtonCropArea, mouseDownImageNewSongAlert.PixelFormat);
yueYuButtonNewSong.BackgroundImage = yueYuNewSongNormalBackground;
yueYuButtonNewSong.BackgroundImageLayout = ImageLayout.Stretch;
yueYuButtonNewSong.FlatStyle = FlatStyle.Flat;
yueYuButtonNewSong.FlatAppearance.BorderSize = 0;
yueYuButtonNewSong.Click += YueYuButtonNewSong_Click;
this.Controls.Add(yueYuButtonNewSong);
yingWenButtonNewSong = new Button { Text = "英文2", Visible = false };
ResizeAndPositionButton(yingWenButtonNewSong, 1214, 418, 209, 59);
Rectangle yingWenNewSongButtonCropArea = new Rectangle(1214, 418, 209, 59);
yingWenNewSongNormalBackground = normalStateImageNewSongAlert.Clone(yingWenNewSongButtonCropArea, normalStateImageNewSongAlert.PixelFormat);
yingWenNewSongActiveBackground = mouseDownImageNewSongAlert.Clone(yingWenNewSongButtonCropArea, mouseDownImageNewSongAlert.PixelFormat);
yingWenButtonNewSong.BackgroundImage = yingWenNewSongNormalBackground;
yingWenButtonNewSong.BackgroundImageLayout = ImageLayout.Stretch;
yingWenButtonNewSong.FlatStyle = FlatStyle.Flat;
yingWenButtonNewSong.FlatAppearance.BorderSize = 0;
yingWenButtonNewSong.Click += YingWenButtonNewSong_Click;
this.Controls.Add(yingWenButtonNewSong);
riYuButtonNewSong = new Button { Text = "日語2", Visible = false };
ResizeAndPositionButton(riYuButtonNewSong, 1214, 481, 209, 59);
Rectangle riYuNewSongButtonCropArea = new Rectangle(1214, 481, 209, 59);
riYuNewSongNormalBackground = normalStateImageNewSongAlert.Clone(riYuNewSongButtonCropArea, normalStateImageNewSongAlert.PixelFormat);
riYuNewSongActiveBackground = mouseDownImageNewSongAlert.Clone(riYuNewSongButtonCropArea, mouseDownImageNewSongAlert.PixelFormat);
riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground;
riYuButtonNewSong.BackgroundImageLayout = ImageLayout.Stretch;
riYuButtonNewSong.FlatStyle = FlatStyle.Flat;
riYuButtonNewSong.FlatAppearance.BorderSize = 0;
riYuButtonNewSong.Click += RiYuButtonNewSong_Click;
this.Controls.Add(riYuButtonNewSong);
hanYuButtonNewSong = new Button { Text = "韓語2", Visible = false };
ResizeAndPositionButton(hanYuButtonNewSong, 1214, 544, 209, 58);
Rectangle hanYuNewSongButtonCropArea = new Rectangle(1214, 544, 209, 58);
hanYuNewSongNormalBackground = normalStateImageNewSongAlert.Clone(hanYuNewSongButtonCropArea, normalStateImageNewSongAlert.PixelFormat);
hanYuNewSongActiveBackground = mouseDownImageNewSongAlert.Clone(hanYuNewSongButtonCropArea, mouseDownImageNewSongAlert.PixelFormat);
hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground;
hanYuButtonNewSong.BackgroundImageLayout = ImageLayout.Stretch;
hanYuButtonNewSong.FlatStyle = FlatStyle.Flat;
hanYuButtonNewSong.FlatAppearance.BorderSize = 0;
hanYuButtonNewSong.Click += HanYuButtonNewSong_Click;
this.Controls.Add(hanYuButtonNewSong);
}
public static int ReadNewSongLimit()
{
string filePath = Path.Combine(Application.StartupPath, "SongLimitsSettings.txt");
try
{
var lines = File.ReadAllLines(filePath);
foreach (var line in lines)
{
if (line.StartsWith("NewSongLimit:"))
{
string valuePart = line.Split(':')[1].Trim();
int limit;
if (int.TryParse(valuePart, out limit))
{
return limit;
}
break;
}
}
}
catch (Exception ex)
{
Console.WriteLine("Failed to read song limits from file: " + ex.Message);
return 100;
}
return 100;
}
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void YueYuButtonNewSong_Click(object sender, EventArgs e)
{
guoYuButtonNewSong.BackgroundImage = guoYuNewSongNormalBackground;
taiYuButtonNewSong.BackgroundImage = taiYuNewSongNormalBackground;
yueYuButtonNewSong.BackgroundImage = yueYuNewSongActiveBackground;
yingWenButtonNewSong.BackgroundImage = yingWenNewSongNormalBackground;
riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground;
hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground;
int songLimit = ReadNewSongLimit();
yueYuSongs2 = allSongs.Where(song => song.Category == "粵語")
.OrderByDescending(song => song.AddedTime)
.Take(songLimit)
.ToList();
currentPage = 0;
currentSongList = yueYuSongs2;
totalPages = (int)Math.Ceiling((double)yueYuSongs2.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void GuoYuButtonNewSong_Click(object sender, EventArgs e)
{
guoYuButtonNewSong.BackgroundImage = guoYuNewSongActiveBackground;
taiYuButtonNewSong.BackgroundImage = taiYuNewSongNormalBackground;
yueYuButtonNewSong.BackgroundImage = yueYuNewSongNormalBackground;
yingWenButtonNewSong.BackgroundImage = yingWenNewSongNormalBackground;
riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground;
hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground;
int songLimit = ReadNewSongLimit();
guoYuSongs2 = allSongs.Where(song => song.Category == "國語")
.OrderByDescending(song => song.AddedTime)
.Take(songLimit)
.ToList();
currentPage = 0;
currentSongList = guoYuSongs2;
totalPages = (int)Math.Ceiling((double)guoYuSongs2.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void YingWenButtonNewSong_Click(object sender, EventArgs e)
{
guoYuButtonNewSong.BackgroundImage = guoYuNewSongNormalBackground;
taiYuButtonNewSong.BackgroundImage = taiYuNewSongNormalBackground;
yueYuButtonNewSong.BackgroundImage = yueYuNewSongNormalBackground;
yingWenButtonNewSong.BackgroundImage = yingWenNewSongActiveBackground;
riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground;
hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground;
int songLimit = ReadNewSongLimit();
yingWenSongs2 = allSongs.Where(song => song.Category == "英文")
.OrderByDescending(song => song.AddedTime)
.Take(songLimit)
.ToList();
currentPage = 0;
currentSongList = yingWenSongs2;
totalPages = (int)Math.Ceiling((double)yingWenSongs2.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void RiYuButtonNewSong_Click(object sender, EventArgs e)
{
guoYuButtonNewSong.BackgroundImage = guoYuNewSongNormalBackground;
taiYuButtonNewSong.BackgroundImage = taiYuNewSongNormalBackground;
yueYuButtonNewSong.BackgroundImage = yueYuNewSongNormalBackground;
yingWenButtonNewSong.BackgroundImage = yingWenNewSongNormalBackground;
riYuButtonNewSong.BackgroundImage = riYuNewSongActiveBackground;
hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground;
int songLimit = ReadNewSongLimit();
riYuSongs2 = allSongs.Where(song => song.Category == "日語")
.OrderByDescending(song => song.AddedTime)
.Take(songLimit)
.ToList();
currentPage = 0;
currentSongList = riYuSongs2;
totalPages = (int)Math.Ceiling((double)riYuSongs2.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void HanYuButtonNewSong_Click(object sender, EventArgs e)
{
guoYuButtonNewSong.BackgroundImage = guoYuNewSongNormalBackground;
taiYuButtonNewSong.BackgroundImage = taiYuNewSongNormalBackground;
yueYuButtonNewSong.BackgroundImage = yueYuNewSongNormalBackground;
yingWenButtonNewSong.BackgroundImage = yingWenNewSongNormalBackground;
riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground;
hanYuButtonNewSong.BackgroundImage = hanYuNewSongActiveBackground;
int songLimit = ReadNewSongLimit();
hanYuSongs2 = allSongs.Where(song => song.Category == "韓語")
.OrderByDescending(song => song.AddedTime)
.Take(songLimit)
.ToList();
currentPage = 0;
currentSongList = hanYuSongs2;
totalPages = (int)Math.Ceiling((double)hanYuSongs2.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Linq;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private void TaiYuButtonNewSong_Click(object sender, EventArgs e)
{
guoYuButtonNewSong.BackgroundImage = guoYuNewSongNormalBackground;
taiYuButtonNewSong.BackgroundImage = taiYuNewSongActiveBackground;
yueYuButtonNewSong.BackgroundImage = yueYuNewSongNormalBackground;
yingWenButtonNewSong.BackgroundImage = yingWenNewSongNormalBackground;
riYuButtonNewSong.BackgroundImage = riYuNewSongNormalBackground;
hanYuButtonNewSong.BackgroundImage = hanYuNewSongNormalBackground;
int songLimit = ReadNewSongLimit();
taiYuSongs2 = allSongs.Where(song => song.Category == "台語")
.OrderByDescending(song => song.AddedTime)
.Take(songLimit)
.ToList();
currentPage = 0;
currentSongList = taiYuSongs2;
totalPages = (int)Math.Ceiling((double)taiYuSongs2.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
}
}

View File

@ -0,0 +1,416 @@
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Collections.Generic;
using DBObj;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private Button myFavoritesButton;
private Bitmap myFavoritesNormalBackground;
private Bitmap myFavoritesActiveBackground;
private string mobileNumber = string.Empty;
public static bool isPhoneNumberValid;
private bool showError = false;
private PictureBox FavoritePictureBox;
private Button[] favoriteNumberButton;
private Button enterFavoriteButton;
private Button newFavoriteButton;
private Button refillFavoriteButton;
private Button closeFavoriteButton;
private Label errorMessageLabel;
private void InitializeButtonsForFavoritePictureBox()
{
int[,] coords = new int[,]
{
{799, 508, 70, 65},
{878, 508, 70, 65},
{957, 508, 70, 65},
{1036, 508, 70, 65},
{1115, 508, 70, 65},
{799, 580, 70, 65},
{878, 580, 70, 65},
{957, 580, 70, 65},
{1036, 580, 70, 65},
{1115, 580, 70, 65}
};
int screenW = Screen.PrimaryScreen.Bounds.Width;
int screenH = Screen.PrimaryScreen.Bounds.Height;
float widthRatio = screenW / (float)1440;
float heightRatio = screenH / (float)900;
favoriteNumberButton = new Button[10];
for (int i = 0; i < favoriteNumberButton.Length; i++)
{
favoriteNumberButton[i] = new Button();
ResizeAndPositionButton(favoriteNumberButton[i], coords[i, 0], coords[i, 1], coords[i, 2], coords[i, 3]);
string fileName = (i + 2).ToString("00");
string filePath = Path.Combine(Application.StartupPath, @"themes\superstar\我的最愛\我的最愛-" + fileName + ".jpg");
favoriteNumberButton[i].BackgroundImage = Image.FromFile(filePath);
favoriteNumberButton[i].BackgroundImageLayout = ImageLayout.Stretch;
favoriteNumberButton[i].FlatStyle = FlatStyle.Flat;
favoriteNumberButton[i].FlatAppearance.BorderSize = 0;
favoriteNumberButton[i].BackColor = Color.Transparent;
favoriteNumberButton[i].FlatAppearance.MouseDownBackColor = Color.Transparent;
favoriteNumberButton[i].FlatAppearance.MouseOverBackColor = Color.Transparent;
favoriteNumberButton[i].Name = "favoriteNumberButton" + i;
favoriteNumberButton[i].Tag = (i + 1).ToString();
if (i == 9)
{
favoriteNumberButton[i].Name = "favoriteNumberButton0";
favoriteNumberButton[i].Tag = "0";
}
favoriteNumberButton[i].Click += FavoriteNumberButton_Click;
this.Controls.Add(favoriteNumberButton[i]);
}
enterFavoriteButton = new Button()
{
Name = "enterFavoriteButton"
};
ResizeAndPositionButton(enterFavoriteButton, 842, 652, 70, 65);
enterFavoriteButton.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, @"themes\superstar\我的最愛\我的最愛-12.jpg"));
enterFavoriteButton.BackgroundImageLayout = ImageLayout.Stretch;
enterFavoriteButton.FlatStyle = FlatStyle.Flat;
enterFavoriteButton.FlatAppearance.BorderSize = 0;
enterFavoriteButton.BackColor = Color.Transparent;
enterFavoriteButton.FlatAppearance.MouseDownBackColor = Color.Transparent;
enterFavoriteButton.FlatAppearance.MouseOverBackColor = Color.Transparent;
enterFavoriteButton.Click += EnterFavoriteButton_Click;
newFavoriteButton = new Button()
{
Name = "newFavoriteButton"
};
ResizeAndPositionButton(newFavoriteButton, 921, 652, 70, 65);
newFavoriteButton.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, @"themes\superstar\我的最愛\我的最愛-13.jpg"));
newFavoriteButton.BackgroundImageLayout = ImageLayout.Stretch;
newFavoriteButton.FlatStyle = FlatStyle.Flat;
newFavoriteButton.FlatAppearance.BorderSize = 0;
newFavoriteButton.BackColor = Color.Transparent;
newFavoriteButton.FlatAppearance.MouseDownBackColor = Color.Transparent;
newFavoriteButton.FlatAppearance.MouseOverBackColor = Color.Transparent;
newFavoriteButton.Click += NewFavoriteButton_Click;
refillFavoriteButton = new Button()
{
Name = "refillFavoriteButton"
};
ResizeAndPositionButton(refillFavoriteButton, 999, 652, 70, 65);
refillFavoriteButton.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, @"themes\superstar\我的最愛\我的最愛-14.jpg"));
refillFavoriteButton.BackgroundImageLayout = ImageLayout.Stretch;
refillFavoriteButton.FlatStyle = FlatStyle.Flat;
refillFavoriteButton.FlatAppearance.BorderSize = 0;
refillFavoriteButton.BackColor = Color.Transparent;
refillFavoriteButton.FlatAppearance.MouseDownBackColor = Color.Transparent;
refillFavoriteButton.FlatAppearance.MouseOverBackColor = Color.Transparent;
refillFavoriteButton.Click += RefillFavoriteButton_Click;
closeFavoriteButton = new Button()
{
Name = "closeFavoriteButton"
};
ResizeAndPositionButton(closeFavoriteButton, 1078, 652, 70, 65);
closeFavoriteButton.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, @"themes\superstar\我的最愛\我的最愛-15.jpg"));
closeFavoriteButton.BackgroundImageLayout = ImageLayout.Stretch;
closeFavoriteButton.FlatStyle = FlatStyle.Flat;
closeFavoriteButton.FlatAppearance.BorderSize = 0;
closeFavoriteButton.BackColor = Color.Transparent;
closeFavoriteButton.FlatAppearance.MouseDownBackColor = Color.Transparent;
closeFavoriteButton.FlatAppearance.MouseOverBackColor = Color.Transparent;
closeFavoriteButton.Click += CloseFavoriteButton_Click;
errorMessageLabel = new Label
{
Text = "",
ForeColor = Color.Black,
Location = new Point(10, 250),
AutoSize = true
};
this.Controls.Add(enterFavoriteButton);
this.Controls.Add(newFavoriteButton);
this.Controls.Add(refillFavoriteButton);
this.Controls.Add(closeFavoriteButton);
this.Controls.Add(errorMessageLabel);
}
private void FavoriteNumberButton_Click(object sender, EventArgs e)
{
Button clickedButton = sender as Button;
if (clickedButton != null)
{
mobileNumber += clickedButton.Tag.ToString();
Console.WriteLine("Number button clicked: " + clickedButton.Tag.ToString());
phonenumber = mobileNumber;
FavoritePictureBox.Invalidate();
}
}
public static string phonenumber;
private void FavoritePictureBox_Paint(object sender, PaintEventArgs e)
{
if (!string.IsNullOrEmpty(mobileNumber))
{
using (Font font = new Font("Arial", 24))
using (Brush brush = new SolidBrush(Color.Black))
{
int x = 16;
int y = 68;
if (showError)
{
string errorMessage;
if (!isPhoneNumberValid)
{
errorMessage = "查無此手機號碼!!!";
}
else
{
errorMessage = "手機號碼輸入錯誤!!!";
}
e.Graphics.DrawString(errorMessage, font, brush, x, y);
}
else
{
e.Graphics.DrawString(mobileNumber, font, brush, x, y);
}
}
}
}
private void EnterFavoriteButton_Click(object sender, EventArgs e)
{
if (mobileNumber.StartsWith("09") && mobileNumber.Length == 10)
{
if (SongListManager.Instance.CheckIfPhoneNumberExists(mobileNumber))
{
isPhoneNumberValid = true;
SongListManager.Instance.UserLogin(mobileNumber);
ToggleFavoritePictureBoxButtonsVisibility();
}
else
{
isPhoneNumberValid = false;
showError = true;
FavoritePictureBox.Invalidate();
FavoritePictureBox.Refresh();
}
}
else
{
showError = true;
isPhoneNumberValid = true;
FavoritePictureBox.Invalidate();
FavoritePictureBox.Refresh();
}
}
private void NewFavoriteButton_Click(object sender, EventArgs e)
{
if (mobileNumber.StartsWith("09") && mobileNumber.Length == 10)
{
if (SongListManager.Instance.CheckIfPhoneNumberExists(mobileNumber))
{
isPhoneNumberValid = true;
SongListManager.Instance.UserLogin(mobileNumber);
ToggleFavoritePictureBoxButtonsVisibility();
}
else
{
isPhoneNumberValid = true;
SongListManager.Instance.AddNewUser(mobileNumber);
SongListManager.Instance.UserLogin(mobileNumber);
List<SongData> emptySongList = new List<SongData> { new SongData("", "", "歡迎光臨 " + "(" + mobileNumber + ")", 0, "", "", "", "", DateTime.Now, "", "", "", "", "", "", "", "", "", "", "", "", 1) };
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(emptySongList);
ToggleFavoritePictureBoxButtonsVisibility();
}
}
else
{
showError = true;
isPhoneNumberValid = true;
FavoritePictureBox.Invalidate();
FavoritePictureBox.Refresh();
}
}
private void RefillFavoriteButton_Click(object sender, EventArgs e)
{
mobileNumber = string.Empty;
showError = false;
FavoritePictureBox.Invalidate();
FavoritePictureBox.Refresh();
SongListManager.Instance.IsUserLoggedIn = false;
SongListManager.Instance.UserPhoneNumber = string.Empty;
}
private void CloseFavoriteButton_Click(object sender, EventArgs e)
{
ToggleFavoritePictureBoxButtonsVisibility();
}
private void MyFavoritesButton_Click(object sender, EventArgs e)
{
newSongAlertButton.BackgroundImage = newSongAlertNormalBackground;
hotPlayButton.BackgroundImage = hotPlayNormalBackground;
singerSearchButton.BackgroundImage = singerSearchNormalBackground;
songSearchButton.BackgroundImage = songSearchNormalBackground;
languageSearchButton.BackgroundImage = languageSearchNormalBackground;
groupSearchButton.BackgroundImage = groupSearchNormalBackground;
categorySearchButton.BackgroundImage = categorySearchNormalBackground;
orderedSongsButton.BackgroundImage = orderedSongsNormalBackground;
myFavoritesButton.BackgroundImage = myFavoritesActiveBackground;
promotionsButton.BackgroundImage = promotionsNormalBackground;
deliciousFoodButton.BackgroundImage = deliciousFoodNormalBackground;
isOnOrderedSongsPage = false;
if (!FavoritePictureBox.Visible)
{
ShowImageOnFavoritePictureBox(Path.Combine(Application.StartupPath, @"themes\superstar\其他介面\其他_我的最愛.jpg"));
SetFavoritePictureBoxAndButtonsVisibility(true);
}
else
{
ToggleFavoritePictureBoxButtonsVisibility();
}
if (pictureBoxQRCode != null)
{
pictureBoxQRCode.Visible = false;
closeQRCodeButton.Visible = false;
}
SetPictureBoxToggleLightAndButtonsVisibility(false);
SetPictureBoxSceneSoundEffectsAndButtonsVisibility(false);
}
private void ShowImageOnFavoritePictureBox(string imagePath)
{
Bitmap originalImage = new Bitmap(imagePath);
Console.WriteLine(String.Format("Original Image Size: {0}x{1}", originalImage.Width, originalImage.Height));
Rectangle cropArea = new Rectangle(784, 393, 555, 442);
Bitmap croppedImage = CropImage(originalImage, cropArea);
FavoritePictureBox.Image = croppedImage;
ResizeAndPositionPictureBox(FavoritePictureBox, cropArea.X, cropArea.Y, 416, 323);
FavoritePictureBox.Visible = true;
}
private void ToggleFavoritePictureBoxButtonsVisibility()
{
bool areButtonsVisible = FavoritePictureBox.Visible;
SetFavoritePictureBoxAndButtonsVisibility(!areButtonsVisible);
}
private void SetFavoritePictureBoxAndButtonsVisibility(bool isVisible)
{
System.Action action = () =>
{
SuspendLayout();
FavoritePictureBox.Visible = isVisible;
if (isVisible) FavoritePictureBox.BringToFront();
enterFavoriteButton.Visible = isVisible;
newFavoriteButton.Visible = isVisible;
refillFavoriteButton.Visible = isVisible;
closeFavoriteButton.Visible = isVisible;
if (isVisible)
{
enterFavoriteButton.BringToFront();
newFavoriteButton.BringToFront();
refillFavoriteButton.BringToFront();
closeFavoriteButton.BringToFront();
}
foreach (Button button in favoriteNumberButton)
{
button.Visible = isVisible;
if (isVisible)
button.BringToFront();
}
ResumeLayout();
};
if (this.InvokeRequired)
{
this.Invoke(action);
}
else
{
action();
}
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.IO;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private Button orderedSongsButton;
private Bitmap orderedSongsNormalBackground;
private Bitmap orderedSongsActiveBackground;
}
}

View File

@ -0,0 +1,173 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private Button promotionsButton;
private Bitmap promotionsNormalBackground;
private Bitmap promotionsActiveBackground;
private Button previousPromotionButton;
private Button nextPromotionButton;
private Button closePromotionsButton;
private void InitializePromotionsButton()
{
try
{
// 設定基礎位置和間距
int baseX = Screen.PrimaryScreen.Bounds.Width - 300;
int baseY = Screen.PrimaryScreen.Bounds.Height - 120;
int buttonSpacing = 90;
// 共用的按鈕設置
void ConfigurePromotionButton(Button button, string imagePath, Point location)
{
button.Size = new Size(80, 80);
button.BackColor = Color.Transparent;
button.FlatStyle = FlatStyle.Flat;
button.FlatAppearance.BorderSize = 0;
button.FlatAppearance.MouseOverBackColor = Color.Transparent;
button.FlatAppearance.MouseDownBackColor = Color.Transparent;
button.Location = location;
using (var stream = new MemoryStream(File.ReadAllBytes(Path.Combine(Application.StartupPath, imagePath))))
{
var image = Image.FromStream(stream);
if (image.PixelFormat != System.Drawing.Imaging.PixelFormat.Format32bppArgb)
{
var bitmap = new Bitmap(image.Width, image.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var g = Graphics.FromImage(bitmap))
{
g.Clear(Color.Transparent);
g.DrawImage(image, 0, 0, image.Width, image.Height);
}
button.BackgroundImage = bitmap;
}
else
{
button.BackgroundImage = image;
}
}
button.BackgroundImageLayout = ImageLayout.Stretch;
}
// 配置各個按鈕
previousPromotionButton = new Button { Name = "previousPromotionButton", Visible = true };
ConfigurePromotionButton(previousPromotionButton, "themes\\superstar\\上一頁.png", new Point(baseX, baseY));
previousPromotionButton.Click += PreviousPromotionButton_Click;
closePromotionsButton = new Button { Name = "closePromotionsButton", Visible = false };
ConfigurePromotionButton(closePromotionsButton, "themes\\superstar\\退出.png", new Point(baseX + buttonSpacing, baseY));
closePromotionsButton.Click += ClosePromotionsButton_Click;
nextPromotionButton = new Button { Name = "nextPromotionButton", Visible = true };
ConfigurePromotionButton(nextPromotionButton, "themes\\superstar\\下一頁.png", new Point(baseX + (buttonSpacing * 2), baseY));
nextPromotionButton.Click += NextPromotionButton_Click;
this.Controls.Add(previousPromotionButton);
this.Controls.Add(closePromotionsButton);
this.Controls.Add(nextPromotionButton);
}
catch (Exception ex)
{
MessageBox.Show($"初始化按鈕時發生錯誤: {ex.Message}");
}
}
private List<Image> LoadPromotionsImages()
{
List<Image> images = new List<Image>();
string newsFolderPath = Path.Combine(Application.StartupPath, "news");
string[] imageFiles = Directory.GetFiles(newsFolderPath, "*.jpg");
foreach (string filePath in imageFiles)
{
try
{
images.Add(Image.FromFile(filePath));
}
catch (Exception ex)
{
Console.WriteLine("Error loading image: " + filePath + ". Exception: " + ex.Message);
}
}
return images;
}
private void promotionsButton_Click(object sender, EventArgs e)
{
newSongAlertButton.BackgroundImage = newSongAlertNormalBackground;
hotPlayButton.BackgroundImage = hotPlayNormalBackground;
singerSearchButton.BackgroundImage = singerSearchNormalBackground;
songSearchButton.BackgroundImage = songSearchNormalBackground;
languageSearchButton.BackgroundImage = languageSearchNormalBackground;
groupSearchButton.BackgroundImage = groupSearchNormalBackground;
categorySearchButton.BackgroundImage = categorySearchNormalBackground;
orderedSongsButton.BackgroundImage = orderedSongsNormalBackground;
myFavoritesButton.BackgroundImage = myFavoritesNormalBackground;
promotionsButton.BackgroundImage = promotionsActiveBackground;
deliciousFoodButton.BackgroundImage = deliciousFoodNormalBackground;
isOnOrderedSongsPage = false;
promotionsAndMenuPanel.LoadImages(promotions);
promotionsAndMenuPanel.Visible = true;
promotionsAndMenuPanel.BringToFront();
previousPromotionButton.Visible = true;
previousPromotionButton.BringToFront();
nextPromotionButton.Visible = true;
nextPromotionButton.BringToFront();
closePromotionsButton.Visible = true;
closePromotionsButton.BringToFront();
if (pictureBoxQRCode != null)
{
pictureBoxQRCode.Visible = false;
closeQRCodeButton.Visible = false;
}
SetPictureBoxToggleLightAndButtonsVisibility(false);
}
private void PreviousPromotionButton_Click(object sender, EventArgs e)
{
promotionsAndMenuPanel.LoadPreviousPage();
}
private void NextPromotionButton_Click(object sender, EventArgs e)
{
promotionsAndMenuPanel.LoadNextPage();
}
private void ClosePromotionsButton_Click(object sender, EventArgs e)
{
promotionsAndMenuPanel.Visible = false;
previousPromotionButton.Visible = false;
nextPromotionButton.Visible = false;
closePromotionsButton.Visible = false;
HotPlayButton_Click(sender, e);
}
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private PromotionsAndMenuPanel promotionsAndMenuPanel;
private void InitializePromotionsAndMenuPanel()
{
promotionsAndMenuPanel = new PromotionsAndMenuPanel();
ResizeAndPositionControl(promotionsAndMenuPanel, 0, 0, 1440, 900);
this.Controls.Add(promotionsAndMenuPanel);
promotions = LoadPromotionsImages();
menu = LoadMenuImages();
}
private List<Image> LoadMenuImages()
{
List<Image> images = new List<Image>();
string foodsFolderPath = Path.Combine(Application.StartupPath, "foods");
string[] imageFiles = Directory.GetFiles(foodsFolderPath, "*.jpg");
foreach (string filePath in imageFiles)
{
try
{
images.Add(Image.FromFile(filePath));
}
catch (Exception ex)
{
Console.WriteLine("Error loading image: " + filePath + ". Exception: " + ex.Message);
}
}
return images;
}
}
}

View File

@ -0,0 +1,142 @@
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
namespace DualScreenDemo
{
public partial class PrimaryForm : Form
{
private PictureBox pictureBoxQRCode;
private Button closeQRCodeButton;
private void OverlayQRCodeOnImage(string randomFolderPath)
{
try
{
string imagePath = Path.Combine(Application.StartupPath, "themes/superstar/cropped_qrcode.jpg");
if (!File.Exists(imagePath))
{
Console.WriteLine("Base image not found: " + imagePath);
return;
}
using (Image baseImage = Image.FromFile(imagePath))
{
string serverAddressFilePath = Path.Combine(Application.StartupPath, "txt", "ip.txt");
if (!File.Exists(serverAddressFilePath))
{
Console.WriteLine("Server address file not found: " + serverAddressFilePath);
return;
}
string serverAddress = File.ReadAllText(serverAddressFilePath).Trim();
// 根据地址格式生成不同的URL
string qrContent = serverAddress.Contains(":") ?
String.Format("http://{0}/{1}/windows.html", serverAddress, randomFolderPath) :
String.Format("http://{0}:{1}/{2}/windows.html", serverAddress, 9090, randomFolderPath);
Console.WriteLine("QR Content: " + qrContent);
string qrImagePath = Path.Combine(Application.StartupPath, "themes/superstar/_www", randomFolderPath, "qrcode.png");
if (!File.Exists(qrImagePath))
{
Console.WriteLine("QR code image not found: " + qrImagePath);
return;
}
Image qrCodeImage = null;
for (int i = 0; i < 3; i++)
{
try
{
using (var fs = new FileStream(qrImagePath, FileMode.Open, FileAccess.Read))
{
qrCodeImage = Image.FromStream(fs);
}
break;
}
catch (Exception ex)
{
Console.WriteLine("Error loading QR code image: " + ex.Message);
System.Threading.Thread.Sleep(100);
}
}
if (qrCodeImage == null)
{
Console.WriteLine("Failed to load QR code image after multiple attempts.");
return;
}
using (qrCodeImage)
{
using (Bitmap bitmap = new Bitmap(baseImage.Width, baseImage.Height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.DrawImage(baseImage, 0, 0);
Rectangle qrCodeRect = new Rectangle(32, 39, 165, 165);
g.DrawImage(qrCodeImage, qrCodeRect);
}
pictureBoxQRCode.Image = new Bitmap(bitmap);
}
}
}
ResizeAndPositionControl(pictureBoxQRCode, 975, 442, 226, 274);
Bitmap originalImage = new Bitmap(Path.Combine(Application.StartupPath, "themes\\superstar\\cropped_qrcode.jpg"));
Rectangle closeQRCodeCropArea = new Rectangle(198, 6, 22, 22);
Bitmap closeQRCodeCroppedImage = new Bitmap(closeQRCodeCropArea.Width, closeQRCodeCropArea.Height);
using (Graphics g = Graphics.FromImage(closeQRCodeCroppedImage))
{
g.DrawImage(originalImage, new Rectangle(0, 0, closeQRCodeCropArea.Width, closeQRCodeCropArea.Height), closeQRCodeCropArea, GraphicsUnit.Pixel);
}
closeQRCodeButton = new Button { Text = "" };
closeQRCodeButton.Name = "closeQRCodeButton";
ResizeAndPositionButton(closeQRCodeButton, 1173, 448, 22, 22);
closeQRCodeButton.BackgroundImage = closeQRCodeCroppedImage;
closeQRCodeButton.BackgroundImageLayout = ImageLayout.Stretch;
closeQRCodeButton.FlatStyle = FlatStyle.Flat;
closeQRCodeButton.FlatAppearance.BorderSize = 0;
closeQRCodeButton.Click += CloseQRCodeButton_Click;
this.Controls.Add(closeQRCodeButton);
}
catch (Exception ex)
{
Console.WriteLine("Error in OverlayQRCodeOnImage: " + ex.Message);
if (ex.InnerException != null)
{
Console.WriteLine("Inner exception: " + ex.InnerException.Message);
}
}
}
private void CloseQRCodeButton_Click(object sender, EventArgs e)
{
pictureBoxQRCode.Visible = false;
closeQRCodeButton.Visible = false;
}
}
}

View File

@ -0,0 +1,205 @@
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using NAudio.Wave;
using WMPLib;
using System.Collections.Generic;
namespace DualScreenDemo
{
public partial class PrimaryForm : Form
{
private WindowsMediaPlayer mediaPlayer;
private IWavePlayer waveOut;
private AudioFileReader audioFileReader;
private PictureBox pictureBoxSceneSoundEffects;
private Button constructionButton;
private Button marketButton;
private Button drivingButton;
private Button airportButton;
private Button officeButton;
private Button closeButton;
private void InitializeMediaPlayer()
{
mediaPlayer = new WindowsMediaPlayer();
}
private void InitializeSoundEffectButtons()
{
constructionButton = new Button
{
Name = "constructionButton",
};
ConfigureButton(constructionButton, 876, 494, 148, 64,
resizedNormalStateImageForSceneSoundEffects, resizedNormalStateImageForSceneSoundEffects, resizedNormalStateImageForSceneSoundEffects,
ConstructionButton_Click);
this.Controls.Add(constructionButton);
marketButton = new Button
{
Name = "marketButton",
};
ConfigureButton(marketButton, 1037, 495, 148, 63,
resizedNormalStateImageForSceneSoundEffects, resizedNormalStateImageForSceneSoundEffects, resizedNormalStateImageForSceneSoundEffects,
MarketButton_Click);
this.Controls.Add(marketButton);
drivingButton = new Button
{
Name = "drivingButton",
};
ConfigureButton(drivingButton, 876, 570, 148, 63,
resizedNormalStateImageForSceneSoundEffects, resizedNormalStateImageForSceneSoundEffects, resizedNormalStateImageForSceneSoundEffects,
DrivingButton_Click);
this.Controls.Add(drivingButton);
airportButton = new Button
{
Name = "airportButton",
};
ConfigureButton(airportButton, 1037, 570, 148, 63,
resizedNormalStateImageForSceneSoundEffects, resizedNormalStateImageForSceneSoundEffects, resizedNormalStateImageForSceneSoundEffects,
AirportButton_Click);
this.Controls.Add(airportButton);
officeButton = new Button
{
Name = "officeButton",
};
ConfigureButton(officeButton, 876, 646, 148, 64,
resizedNormalStateImageForSceneSoundEffects, resizedNormalStateImageForSceneSoundEffects, resizedNormalStateImageForSceneSoundEffects,
OfficeButton_Click);
this.Controls.Add(officeButton);
closeButton = new Button
{
Name = "closeButton",
};
ConfigureButton(closeButton, 1036, 646, 150, 63,
resizedNormalStateImageForSceneSoundEffects, resizedNormalStateImageForSceneSoundEffects, resizedNormalStateImageForSceneSoundEffects,
CloseButton_Click);
this.Controls.Add(closeButton);
}
private void SoundEffectButton_Click(object sender, EventArgs e)
{
SetHotSongButtonsVisibility(false);
SetNewSongButtonsVisibility(false);
SetSingerSearchButtonsVisibility(false);
SetSongSearchButtonsVisibility(false);
if (!pictureBoxSceneSoundEffects.Visible)
{
ShowImageOnPictureBoxSceneSoundEffects(Path.Combine(Application.StartupPath, @"themes\superstar\555022.jpg"));
SetPictureBoxSceneSoundEffectsAndButtonsVisibility(true);
}
else
{
TogglePictureBoxSceneSoundEffectsButtonsVisibility();
}
}
private void ConstructionButton_Click(object sender, EventArgs e) => PlaySound(@"sounds\1857.mp3");
private void MarketButton_Click(object sender, EventArgs e) => PlaySound(@"sounds\13472_Audio Trimmer.mp3");
private void DrivingButton_Click(object sender, EventArgs e) => PlaySound(@"sounds\kc1.mp3");
private void AirportButton_Click(object sender, EventArgs e) => PlayMediaSound(@"sounds\xm2401.m4a");
private void OfficeButton_Click(object sender, EventArgs e) => PlayMediaSound(@"sounds\y1640.m4a");
private void CloseButton_Click(object sender, EventArgs e) => TogglePictureBoxSceneSoundEffectsButtonsVisibility();
private void PlaySound(string filePath)
{
waveOut?.Dispose();
audioFileReader?.Dispose();
waveOut = new WaveOutEvent();
audioFileReader = new AudioFileReader(Path.Combine(Application.StartupPath, filePath));
waveOut.Init(audioFileReader);
waveOut.Play();
}
private void PlayMediaSound(string filePath)
{
mediaPlayer.URL = Path.Combine(Application.StartupPath, filePath);
mediaPlayer.controls.play();
}
public void PlayApplauseSound()
{
mediaPlayer.URL = Path.Combine(Application.StartupPath, "zs.m4a");
mediaPlayer.controls.play();
}
private void ShowImageOnPictureBoxSceneSoundEffects(string imagePath)
{
Bitmap originalImage = new Bitmap(imagePath);
Rectangle cropArea = new Rectangle(859, 427, 342, 295);
Bitmap croppedImage = CropImage(originalImage, cropArea);
pictureBoxSceneSoundEffects.Image = croppedImage;
ResizeAndPositionPictureBox(pictureBoxSceneSoundEffects, cropArea.X, cropArea.Y, cropArea.Width, cropArea.Height);
pictureBoxSceneSoundEffects.Visible = true;
}
private void TogglePictureBoxSceneSoundEffectsButtonsVisibility()
{
bool areButtonsVisible = pictureBoxSceneSoundEffects.Visible;
SetPictureBoxSceneSoundEffectsAndButtonsVisibility(!areButtonsVisible);
}
private void SetPictureBoxSceneSoundEffectsAndButtonsVisibility(bool isVisible)
{
pictureBoxSceneSoundEffects.Visible = isVisible;
if (isVisible)
{
pictureBoxSceneSoundEffects.BringToFront();
}
List<Button> soundEffectButtons = new List<Button>
{
constructionButton,
marketButton,
drivingButton,
airportButton,
officeButton,
closeButton
};
foreach (Button button in soundEffectButtons)
{
button.Visible = isVisible;
if (isVisible)
{
button.BringToFront();
}
}
}
}
}

View File

@ -0,0 +1,161 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using OverlayFormObj;
namespace DualScreenDemo
{
public partial class PrimaryForm : Form
{
public Panel primaryScreenPanel;
public Button syncServiceBellButton;
public Button syncCutSongButton;
public Button syncReplayButton;
public Button syncOriginalSongButton;
public Button syncMuteButton;
public Button syncPauseButton;
public Button syncPlayButton;
public Button syncVolumeUpButton;
public Button syncVolumeDownButton;
public Button syncMicUpButton;
public Button syncMicDownButton;
public Button syncCloseButton;
private void InitializeSyncScreen()
{
this.primaryScreenPanel = new System.Windows.Forms.Panel();
this.syncServiceBellButton = new System.Windows.Forms.Button();
this.syncCutSongButton = new System.Windows.Forms.Button();
this.syncReplayButton = new System.Windows.Forms.Button();
this.syncOriginalSongButton = new System.Windows.Forms.Button();
this.syncMuteButton = new System.Windows.Forms.Button();
this.syncPauseButton = new System.Windows.Forms.Button();
this.syncPlayButton = new System.Windows.Forms.Button();
this.syncVolumeUpButton = new System.Windows.Forms.Button();
this.syncVolumeDownButton = new System.Windows.Forms.Button();
this.syncMicUpButton = new System.Windows.Forms.Button();
this.syncMicDownButton = new System.Windows.Forms.Button();
this.syncCloseButton = new System.Windows.Forms.Button();
this.SuspendLayout();
ResizeAndPositionControl(this.primaryScreenPanel, 0, 0, 1440, 900);
this.primaryScreenPanel.TabIndex = 0;
this.primaryScreenPanel.BorderStyle = BorderStyle.FixedSingle;
this.primaryScreenPanel.BackColor = System.Drawing.Color.Black;
ConfigureButton(this.syncServiceBellButton, 1240, 17, 161, 161,
resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen,
(sender, e) => SendCommandThroughSerialPort("a2 53 a4"));
ConfigureButton(this.syncCutSongButton, 1218, 195, 205, 56, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, (sender, e) => videoPlayerForm.SkipToNextSong());
ConfigureButton(this.syncReplayButton, 1218, 265, 205, 56, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, ReplayButton_Click);
ConfigureButton(this.syncOriginalSongButton, 1218, 335, 205, 56, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, null);
ConfigureButton(this.syncMuteButton, 1218, 406, 205, 55, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, MuteUnmuteButton_Click);
ConfigureButton(this.syncPauseButton, 1218, 475, 205, 56, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, SyncPauseButton_Click);
ConfigureButton(this.syncPlayButton, 1218, 475, 205, 56, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, SyncPlayButton_Click);
ConfigureButton(this.syncVolumeUpButton, 1218, 546, 205, 55, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, null);
this.syncVolumeUpButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowVolumeUpLabel(); volumeUpTimer.Start(); };
this.syncVolumeUpButton.MouseUp += (sender, e) => { OverlayForm.MainForm.HideAllLabels(); volumeUpTimer.Stop(); };
ConfigureButton(this.syncVolumeDownButton, 1218, 616, 205, 55, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, null);
this.syncVolumeDownButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowVolumeDownLabel(); volumeDownTimer.Start(); };
this.syncVolumeDownButton.MouseUp += (sender, e) => { OverlayForm.MainForm.HideAllLabels(); volumeDownTimer.Stop(); };
ConfigureButton(this.syncMicUpButton, 1218, 686, 205, 56, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, null);
this.syncMicUpButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowMicUpLabel(); micControlTimer.Tag = "a2 b5 a4"; micControlTimer.Start(); };
this.syncMicUpButton.MouseUp += (sender, e) => { OverlayForm.MainForm.HideAllLabels(); micControlTimer.Stop(); };
ConfigureButton(this.syncMicDownButton, 1218, 756, 205, 56, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, null);
this.syncMicDownButton.MouseDown += (sender, e) => { OverlayForm.MainForm.ShowMicDownLabel(); micControlTimer.Tag = "a2 b6 a4"; micControlTimer.Start(); };
this.syncMicDownButton.MouseUp += (sender, e) => { OverlayForm.MainForm.HideAllLabels(); micControlTimer.Stop(); };
ConfigureButton(this.syncCloseButton, 1218, 826, 205, 56, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, resizedNormalStateImageForSyncScreen, SyncCloseButton_Click);
this.ClientSize = new System.Drawing.Size(1440, 900);
this.Controls.Add(this.primaryScreenPanel);
this.Controls.Add(this.syncCloseButton);
this.Name = "PrimaryForm";
this.ResumeLayout(false);
}
private void SyncPauseButton_Click(object sender, EventArgs e)
{
videoPlayerForm.Pause();
syncPauseButton.Visible = false;
syncPlayButton.Visible = true;
syncPlayButton.BringToFront();
pauseButton.Visible = false;
playButton.Visible = true;
}
private void SyncPlayButton_Click(object sender, EventArgs e)
{
videoPlayerForm.Play();
syncPauseButton.Visible = true;
syncPauseButton.BringToFront();
syncPlayButton.Visible = false;
pauseButton.Visible = true;
playButton.Visible = false;
}
private void SyncCloseButton_Click(object sender, EventArgs e)
{
VideoPlayerForm.Instance.ClosePrimaryScreenPanel();
}
private void SyncScreenButton_Click(object sender, EventArgs e)
{
videoPlayerForm.IsSyncToPrimaryMonitor = true;
videoPlayerForm.SyncToPrimaryMonitor();
}
}
}

View File

@ -0,0 +1,338 @@
using System;
using System.IO;
using System.Windows.Forms;
using System.Drawing;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private Button btnTurnOn;
private Button btnTurnOff;
private Button btnBright;
private Button btnRomantic;
private Button btnAuto;
private Button btnColorTuning;
private Button btnSoft;
private Button btnDynamic;
private Button btnDeskLamp;
private Button btnStageLight;
private Button btnShelfLight;
private Button btnWallLight;
private Button btnBrightnessUp1;
private Button btnBrightnessDown1;
private Button btnBrightnessUp2;
private Button btnBrightnessDown2;
private PictureBox pictureBoxToggleLight;
private void InitializeButtonsForPictureBoxToggleLight()
{
btnTurnOn = new Button{ Text = "" };
ConfigureButton(btnTurnOn, 604, 410, 122, 62,
resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl,
null);
btnTurnOn.Click += (sender, e) =>
{
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
{
byte[] commandBytes = new byte[] { 0xA2, 0xDB, 0xA4 };
SerialPortManager.mySerialPort.Write(commandBytes, 0, commandBytes.Length);
}
else
{
MessageBox.Show("Serial port is not open. Cannot send track correction command.");
}
};
btnTurnOff = new Button{ Text = "" };
ConfigureButton(btnTurnOff, 753, 411, 122, 62,
resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl,
null);
btnTurnOff.Click += (sender, e) =>
{
SendCommandThroughSerialPort("a2 dc a4");
};
btnBright = new Button{ Text = "" };
ConfigureButton(btnBright, 901, 411, 122, 62,
resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl,
null);
btnBright.Click += (sender, e) =>
{
if (SerialPortManager.mySerialPort != null && SerialPortManager.mySerialPort.IsOpen)
{
byte[] commandBytes = new byte[] { 0xA2, 0xD5, 0xA4 };
SerialPortManager.mySerialPort.Write(commandBytes, 0, commandBytes.Length);
}
else
{
MessageBox.Show("Serial port is not open. Cannot send track correction command.");
}
};
btnRomantic = new Button{ Text = "" };
ConfigureButton(btnRomantic, 1049, 411, 122, 62,
resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl,
(sender, e) => SendCommandThroughSerialPort("a2 d7 a4"));
btnAuto = new Button{ Text = "" };
ConfigureButton(btnAuto, 1049, 494, 123, 63,
resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl,
null);
btnColorTuning = new Button{ Text = "" };
ConfigureButton(btnColorTuning, 1049, 579, 123, 63,
resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl,
(sender, e) => SendCommandThroughSerialPort("a2 75 a4"));
btnSoft = new Button{ Text = "" };
ConfigureButton(btnSoft, 901, 495, 122, 62,
resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl,
(sender, e) => SendCommandThroughSerialPort("a2 d6 a4"));
btnDynamic = new Button{ Text = "" };
ConfigureButton(btnDynamic, 901, 579, 123, 62,
resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl,
(sender, e) => SendCommandThroughSerialPort("a2 d8 a4"));
btnDeskLamp = new Button{ Text = "" };
ConfigureButton(btnDeskLamp, 1048, 662, 124, 64,
resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl,
(sender, e) => SendCommandThroughSerialPort("a2 fb a4"));
btnStageLight = new Button{ Text = "" };
ConfigureButton(btnStageLight, 900, 662, 124, 64,
resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl,
(sender, e) => SendCommandThroughSerialPort("a2 fa a4"));
btnShelfLight = new Button{ Text = "" };
ConfigureButton(btnShelfLight, 752, 662, 124, 64,
resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl,
(sender, e) => SendCommandThroughSerialPort("a2 f9 a4"));
btnWallLight = new Button{ Text = "" };
ConfigureButton(btnWallLight, 604, 662, 124, 64,
resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl,
(sender, e) => SendCommandThroughSerialPort("a2 f8 a4"));
btnBrightnessUp1 = new Button{ Text = "" };
ConfigureButton(btnBrightnessUp1, 603, 495, 122, 62,
resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl,
null);
btnBrightnessUp1.MouseDown += (sender, e) =>
{
lightControlTimer.Tag = "a2 d9 a4";
lightControlTimer.Start();
};
btnBrightnessUp1.MouseUp += (sender, e) =>
{
lightControlTimer.Stop();
};
btnBrightnessDown1 = new Button{ Text = "" };
ConfigureButton(btnBrightnessDown1, 605, 579, 122, 62,
resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl,
null);
btnBrightnessDown1.MouseDown += (sender, e) => { lightControlTimer.Tag = "a2 da a4"; lightControlTimer.Start(); };
btnBrightnessDown1.MouseUp += (sender, e) => { lightControlTimer.Stop(); };
btnBrightnessUp2 = new Button{ Text = "" };
ConfigureButton(btnBrightnessUp2, 753, 495, 123, 62,
resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl,
null);
btnBrightnessUp2.MouseDown += (sender, e) => { lightControlTimer.Tag = "a2 f6 a4"; lightControlTimer.Start(); };
btnBrightnessUp2.MouseUp += (sender, e) => { lightControlTimer.Stop(); };
btnBrightnessDown2 = new Button{ Text = "" };
ConfigureButton(btnBrightnessDown2, 753, 579, 123, 62,
resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl, resizedNormalStateImageForLightControl,
null);
btnBrightnessDown2.MouseDown += (sender, e) => { lightControlTimer.Tag = "a2 f7 a4"; lightControlTimer.Start(); };
btnBrightnessDown2.MouseUp += (sender, e) => { lightControlTimer.Stop(); };
this.Controls.Add(btnTurnOn);
this.Controls.Add(btnTurnOff);
this.Controls.Add(btnBright);
this.Controls.Add(btnRomantic);
this.Controls.Add(btnAuto);
this.Controls.Add(btnColorTuning);
this.Controls.Add(btnSoft);
this.Controls.Add(btnDynamic);
this.Controls.Add(btnDeskLamp);
this.Controls.Add(btnStageLight);
this.Controls.Add(btnShelfLight);
this.Controls.Add(btnWallLight);
this.Controls.Add(btnBrightnessUp1);
this.Controls.Add(btnBrightnessDown1);
this.Controls.Add(btnBrightnessUp2);
this.Controls.Add(btnBrightnessDown2);
}
private void ToggleLightButton_Click(object sender, EventArgs e)
{
SetHotSongButtonsVisibility(false);
SetNewSongButtonsVisibility(false);
SetSingerSearchButtonsVisibility(false);
SetSongSearchButtonsVisibility(false);
if (!pictureBoxToggleLight.Visible)
{
ShowImageOnPictureBoxToggleLight(Path.Combine(Application.StartupPath, @"themes\superstar\選單內介面_燈光控制.jpg"));
SetPictureBoxToggleLightAndButtonsVisibility(true);
}
else
{
TogglePictureBoxToggleLightButtonsVisibility();
}
if (pictureBoxQRCode != null)
{
pictureBoxQRCode.Visible = false;
closeQRCodeButton.Visible = false;
}
}
private void ShowImageOnPictureBoxToggleLight(string imagePath)
{
try
{
if (!File.Exists(imagePath))
{
MessageBox.Show($"找不到圖片文件: {imagePath}");
return;
}
using (var stream = new MemoryStream(File.ReadAllBytes(imagePath)))
{
var originalImage = new Bitmap(stream);
Rectangle cropArea = new Rectangle(570, 359, 630, 379);
using (var croppedImage = new Bitmap(cropArea.Width, cropArea.Height))
{
using (var g = Graphics.FromImage(croppedImage))
{
g.DrawImage(originalImage,
new Rectangle(0, 0, cropArea.Width, cropArea.Height),
cropArea,
GraphicsUnit.Pixel);
}
pictureBoxToggleLight.Image?.Dispose();
pictureBoxToggleLight.Image = new Bitmap(croppedImage);
}
ResizeAndPositionPictureBox(pictureBoxToggleLight, cropArea.X, cropArea.Y, cropArea.Width, cropArea.Height);
pictureBoxToggleLight.Visible = true;
pictureBoxToggleLight.BringToFront();
}
}
catch (Exception ex)
{
MessageBox.Show($"載入燈光控制圖片時發生錯誤: {ex.Message}\n路徑: {imagePath}");
}
}
private void TogglePictureBoxToggleLightButtonsVisibility()
{
bool areButtonsVisible = pictureBoxToggleLight.Visible;
SetPictureBoxToggleLightAndButtonsVisibility(!areButtonsVisible);
}
private void SetPictureBoxToggleLightAndButtonsVisibility(bool isVisible)
{
pictureBoxToggleLight.Visible = isVisible;
btnTurnOn.Visible = isVisible;
btnTurnOff.Visible = isVisible;
btnBright.Visible = isVisible;
btnRomantic.Visible = isVisible;
btnAuto.Visible = isVisible;
btnColorTuning.Visible = isVisible;
btnSoft.Visible = isVisible;
btnDynamic.Visible = isVisible;
btnDeskLamp.Visible = isVisible;
btnStageLight.Visible = isVisible;
btnShelfLight.Visible = isVisible;
btnWallLight.Visible = isVisible;
btnBrightnessUp1.Visible = isVisible;
btnBrightnessDown1.Visible = isVisible;
btnBrightnessUp2.Visible = isVisible;
btnBrightnessDown2.Visible = isVisible;
if (isVisible)
{
pictureBoxToggleLight.BringToFront();
btnTurnOn.BringToFront();
btnTurnOff.BringToFront();
btnBright.BringToFront();
btnRomantic.BringToFront();
btnAuto.BringToFront();
btnColorTuning.BringToFront();
btnSoft.BringToFront();
btnDynamic.BringToFront();
btnDeskLamp.BringToFront();
btnStageLight.BringToFront();
btnShelfLight.BringToFront();
btnWallLight.BringToFront();
btnBrightnessUp1.BringToFront();
btnBrightnessDown1.BringToFront();
btnBrightnessUp2.BringToFront();
btnBrightnessDown2.BringToFront();
}
}
}
}

View File

@ -0,0 +1,212 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using System.Linq;
using DBObj;
using OverlayFormObj;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private Button vodButton;
private Button insertButton;
private Button albumButton;
private Button favoriteButton;
private Panel disabledPanel;
private Button vodScreenCloseButton;
private void InitializeButtonsForVodScreenPictureBox()
{
int screenWidth = 1440;
int screenHeight = 900;
int pictureBoxWidth = 700;
int pictureBoxHeight = 140;
int xPosition = (screenWidth - pictureBoxWidth) / 2;
int yPosition = (screenHeight - pictureBoxHeight) / 2;
vodButton = new Button();
vodButton.Text = "";
ResizeAndPositionButton(vodButton, xPosition + 10, yPosition + 85, 110, 50);
vodButton.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, @"themes\superstar\點播介面\點播介面_點歌.png"));
vodButton.BackgroundImageLayout = ImageLayout.Stretch;
vodButton.FlatStyle = FlatStyle.Flat;
vodButton.FlatAppearance.BorderSize = 0;
vodButton.BackColor = Color.Transparent;
vodButton.FlatAppearance.MouseDownBackColor = Color.Transparent;
vodButton.FlatAppearance.MouseOverBackColor = Color.Transparent;
vodButton.Click += VodButton_Click;
vodButton.Visible = false;
insertButton = new Button();
insertButton.Text = "";
ResizeAndPositionButton(insertButton, xPosition + 135, yPosition + 85, 110, 50);
insertButton.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, @"themes\superstar\點播介面\點播介面_插播.png"));
insertButton.BackgroundImageLayout = ImageLayout.Stretch;
insertButton.FlatStyle = FlatStyle.Flat;
insertButton.FlatAppearance.BorderSize = 0;
insertButton.BackColor = Color.Transparent;
insertButton.FlatAppearance.MouseDownBackColor = Color.Transparent;
insertButton.FlatAppearance.MouseOverBackColor = Color.Transparent;
insertButton.Click += InsertButton_Click;
insertButton.Visible = false;
albumButton = new Button();
albumButton.Text = "";
ResizeAndPositionButton(albumButton, xPosition + 265, yPosition + 85, 140, 50);
albumButton.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, @"themes\superstar\點播介面\點播介面_歷年專輯.png"));
albumButton.BackgroundImageLayout = ImageLayout.Stretch;
albumButton.FlatStyle = FlatStyle.Flat;
albumButton.FlatAppearance.BorderSize = 0;
albumButton.BackColor = Color.Transparent;
albumButton.FlatAppearance.MouseDownBackColor = Color.Transparent;
albumButton.FlatAppearance.MouseOverBackColor = Color.Transparent;
albumButton.Click += AlbumButton_Click;
albumButton.Visible = false;
favoriteButton = new Button();
favoriteButton.Text = "";
ResizeAndPositionButton(favoriteButton, xPosition + 425, yPosition + 85, 140, 50);
favoriteButton.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, @"themes\superstar\點播介面\點播介面_我的最愛.png"));
favoriteButton.BackgroundImageLayout = ImageLayout.Stretch;
favoriteButton.FlatStyle = FlatStyle.Flat;
favoriteButton.FlatAppearance.BorderSize = 0;
favoriteButton.BackColor = Color.Transparent;
favoriteButton.FlatAppearance.MouseDownBackColor = Color.Transparent;
favoriteButton.FlatAppearance.MouseOverBackColor = Color.Transparent;
disabledPanel = new Panel();
disabledPanel.BackColor = Color.FromArgb(128, Color.Black);
disabledPanel.Dock = DockStyle.Fill;
disabledPanel.Visible = !IsUserLoggedIn();
favoriteButton.Controls.Add(disabledPanel);
favoriteButton.Click += FavoriteButton_Click;
if (!IsUserLoggedIn()) {
favoriteButton.Enabled = false;
favoriteButton.BackColor = SystemColors.Control;
}
favoriteButton.Visible = IsUserLoggedIn();
vodScreenCloseButton = new Button();
vodScreenCloseButton.Text = "";
ResizeAndPositionButton(vodScreenCloseButton, xPosition + 580, yPosition + 85, 110, 50);
vodScreenCloseButton.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, @"themes\superstar\點播介面\點播介面_關閉.png"));
vodScreenCloseButton.BackgroundImageLayout = ImageLayout.Stretch;
vodScreenCloseButton.FlatStyle = FlatStyle.Flat;
vodScreenCloseButton.FlatAppearance.BorderSize = 0;
vodScreenCloseButton.BackColor = Color.Transparent;
vodScreenCloseButton.FlatAppearance.MouseDownBackColor = Color.Transparent;
vodScreenCloseButton.FlatAppearance.MouseOverBackColor = Color.Transparent;
vodScreenCloseButton.Click += VodScreenCloseButton_Click;
vodScreenCloseButton.Visible = false;
this.Controls.Add(vodButton);
this.Controls.Add(insertButton);
this.Controls.Add(albumButton);
this.Controls.Add(favoriteButton);
this.Controls.Add(vodScreenCloseButton);
}
private void VodButton_Click(object sender, EventArgs e)
{
OverlayForm.MainForm.AddSongToPlaylist(currentSelectedSong);
SetVodScreenPictureBoxAndButtonsVisibility(false);
}
private void InsertButton_Click(object sender, EventArgs e)
{
OverlayForm.MainForm.InsertSongToPlaylist(currentSelectedSong);
SetVodScreenPictureBoxAndButtonsVisibility(false);
}
private void AlbumButton_Click(object sender, EventArgs e)
{
var selectedSongs = allSongs.Where(song => song.ArtistA == currentSelectedSong.ArtistA)
.OrderByDescending(song => song.AddedTime)
.ToList();
UpdateSongList(selectedSongs);
SetVodScreenPictureBoxAndButtonsVisibility(false);
}
private void FavoriteButton_Click(object sender, EventArgs e)
{
Console.WriteLine("Favorite Button Clicked");
SongListManager.Instance.AddToFavorite(currentSelectedSong.SongNumber);
SetVodScreenPictureBoxAndButtonsVisibility(false);
}
private void VodScreenCloseButton_Click(object sender, EventArgs e)
{
SetVodScreenPictureBoxAndButtonsVisibility(false);
}
private bool IsUserLoggedIn()
{
return SongListManager.Instance.IsUserLoggedIn;
}
private void SetVodScreenPictureBoxAndButtonsVisibility(bool isVisible)
{
overlayPanel.Visible = isVisible;
VodScreenPictureBox.Visible = isVisible;
vodButton.Visible = isVisible;
insertButton.Visible = isVisible;
albumButton.Visible = isVisible;
favoriteButton.Visible = isVisible;
vodScreenCloseButton.Visible = isVisible;
if (isVisible)
{
if (IsUserLoggedIn())
{
favoriteButton.Enabled = true;
favoriteButton.Controls.Remove(disabledPanel);
}
else
{
favoriteButton.Enabled = false;
}
overlayPanel.BringToFront();
VodScreenPictureBox.BringToFront();
vodButton.BringToFront();
insertButton.BringToFront();
albumButton.BringToFront();
favoriteButton.BringToFront();
vodScreenCloseButton.BringToFront();
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,838 @@
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;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private PictureBox pictureBoxZhuYinSingers;
private Button[] phoneticButtonsForSingers;
private Button modifyButtonZhuYinSingers;
private Button clearButtonZhuYinSingers;
private Button closeButtonZhuYinSingers;
private string[] phoneticSymbols;
private (int X, int Y, int Width, int Height)[] phoneticButtonCoords;
private Dictionary<string, (string normal, string mouseDown, string mouseOver)> phoneticButtonImages;
private (int X, int Y, int Width, int Height) modifyButtonZhuYinCoords;
private (int X, int Y, int Width, int Height) clearButtonZhuYinCoords;
private (int X, int Y, int Width, int Height) closeButtonZhuYinCoords;
private RichTextBox inputBoxZhuYinSingers;
private (int X, int Y, int Width, int Height) inputBoxZhuYinCoords;
private string inputBoxFontName;
private float inputBoxFontSize;
private FontStyle inputBoxFontStyle;
private Color inputBoxForeColor;
/// <summary>
/// <para> 點擊「注音歌手搜尋」按鈕時執行的事件處理函式。</para>
/// <para>此函式負責更新按鈕的背景圖片、載入對應的歌手圖片,並切換相關 UI 控件的可見性。</para>
/// </summary>
/// <param name="sender">觸發事件的物件(通常是按鈕本身)。</param>
/// <param name="e">事件參數。</param>
private void ZhuyinSearchSingersButton_Click(object sender, EventArgs e)
{
// 設定按鈕背景,將「注音搜尋」設為啟動狀態,其餘按鈕恢復為正常狀態
zhuyinSearchButton.BackgroundImage = zhuyinSearchActiveBackground;
englishSearchButton.BackgroundImage = englishSearchNormalBackground;
pinyinSearchButton.BackgroundImage = pinyinSearchNormalBackground;
wordCountSearchButton.BackgroundImage = wordCountSearchNormalBackground;
handWritingSearchButton.BackgroundImage = handWritingSearchNormalBackground;
// 載入設定檔,取得圖片路徑資訊
var configData = LoadConfigData();
// 取得「注音歌手圖片」的完整路徑
string imagePath = Path.Combine(Application.StartupPath, configData["ImagePaths"]["ZhuYinSingers"]);
// 在 PictureBox 中顯示對應的「注音歌手」圖片
ShowImageOnPictureBoxZhuYinSingers(Path.Combine(Application.StartupPath, imagePath));
// 設定不同搜尋模式的 UI 控件可見性
SetEnglishSingersAndButtonsVisibility(false); // 隱藏英文字母搜尋相關控件
SetPinYinSingersAndButtonsVisibility(false); // 隱藏拼音搜尋相關控件
SetHandWritingForSingersAndButtonsVisibility(false); // 隱藏手寫搜尋相關控件
SetWordCountSingersAndButtonsVisibility(false); // 隱藏字數搜尋相關控件
SetZhuYinSingersAndButtonsVisibility(true); // 顯示注音搜尋相關控件
//SetPictureBoxArtistSearchAndButtonsVisibility(false); // 隱藏其他搜尋模式的圖片框
ResetinputBox(); // 重置輸入框
// 顯示「注音歌手搜尋」的圖片框
pictureBoxZhuYinSingers.Visible = true;
}
/// <summary>
/// <para>從 config.ini 設定檔中載入注音符號Phonetic Symbols。</para>
/// <para>讀取 ini 檔的 [PhoneticSymbols] 區塊並將「Symbols」欄位的值解析為陣列。</para>
/// </summary>
private void LoadPhoneticSymbolsFromConfig()
{
// 建立 INI 檔案解析器
var parser = new FileIniDataParser();
// 設定檔路徑
string iniFilePath = "config.ini";
IniData data;
// 以 UTF-8 編碼開啟並讀取 INI 檔案
using (var reader = new StreamReader(iniFilePath, Encoding.UTF8))
{
// 解析 INI 檔內容
data = parser.ReadData(reader);
}
// 取得 [PhoneticSymbols] 區塊中的 "Symbols" 欄位內容
string symbols = data["PhoneticSymbols"]["Symbols"];
// 將符號字串以逗號分隔,轉換為字串陣列
phoneticSymbols = symbols.Split(',');
}
/// <summary>
/// 從設定檔 (config.ini) 載入 INI 設定數據。
/// </summary>
/// <returns>回傳解析後的 INI 設定數據 (IniData)。</returns>
private IniData LoadConfigData()
{
var parser = new FileIniDataParser();
string iniFilePath = "config.ini";
// 使用 UTF-8 讀取 INI 檔案並解析內容
using (var reader = new StreamReader(iniFilePath, Encoding.UTF8))
{
return parser.ReadData(reader);
}
}
/// <summary>
/// 從 INI 設定數據中讀取注音符號 (Phonetic Symbols)。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <returns>回傳包含注音符號的字串陣列。</returns>
private string[] LoadPhoneticSymbols(IniData data)
{
// 從 INI 檔案的 [PhoneticSymbols] 區塊取得 Symbols 欄位值
string symbols = data["PhoneticSymbols"]["Symbols"];
// 以逗號分隔字串並轉換為字串陣列
return symbols.Split(',');
}
/// <summary>
/// 從 INI 設定數據中載入按鈕座標資料。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <param name="section">設定檔中按鈕座標所屬的區塊名稱。</param>
/// <param name="buttonCount">按鈕數量。</param>
/// <returns>回傳包含按鈕座標的陣列,每個元素是由 (X, Y, Width, Height) 組成的元組。</returns>
private (int X, int Y, int Width, int Height)[] LoadButtonCoordinates(IniData data, string section, int buttonCount)
{
var buttonList = new List<(int X, int Y, int Width, int Height)>();
// 迴圈讀取每個按鈕的座標設定
for (int i = 1; i <= buttonCount; i++)
{
// 取得按鈕座標的字串 (格式: X,Y,Width,Height)
var coordString = data[section][$"button{i}"];
var coords = coordString.Split(',');
// 將座標資料轉換為 (X, Y, Width, Height) 元組並加入清單
buttonList.Add((
int.Parse(coords[0]), // X 座標
int.Parse(coords[1]), // Y 座標
int.Parse(coords[2]), // 寬度
int.Parse(coords[3]) // 高度
));
}
// 回傳所有按鈕座標的陣列
return buttonList.ToArray();
}
/// <summary>
/// 從 INI 設定數據中載入按鈕圖片檔案路徑資料 (包含正常、點擊、滑鼠移過圖片)。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <param name="section">設定檔中按鈕圖片設定所屬的區塊名稱。</param>
/// <param name="buttonCount">按鈕數量。</param>
/// <returns>回傳一個字典,鍵是按鈕名稱,值是包含正常、點擊和滑鼠移過狀態的元組。</returns>
private Dictionary<string, (string normal, string mouseDown, string mouseOver)> LoadButtonImages(IniData data, string section, int buttonCount)
{
var buttonImages = new Dictionary<string, (string normal, string mouseDown, string mouseOver)>();
// 迴圈讀取每個按鈕的圖片設定
for (int i = 0; i < 35; i++)
{
// 讀取按鈕的三種圖片狀態:正常、點擊、滑鼠移過
buttonImages[$"button{i}"] = (
data[section][$"button{i}_normal"], // 正常狀態圖片路徑
data[section][$"button{i}_mouseDown"], // 點擊狀態圖片路徑
data[section][$"button{i}_mouseOver"] // 滑鼠移過狀態圖片路徑
);
}
// 回傳包含所有按鈕圖片路徑資料的字典
return buttonImages;
}
/// <summary>
/// 從 INI 設定數據中載入特定按鈕的座標資料。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <param name="section">設定檔中按鈕座標所屬的區塊名稱。</param>
/// <param name="buttonKey">指定按鈕的鍵名 (如 "button1")。</param>
/// <returns>回傳包含按鈕座標的元組 (X, Y, Width, Height)。</returns>
private (int X, int Y, int Width, int Height) LoadSpecialButtonCoordinates(IniData data, string section, string buttonKey)
{
// 取得按鈕座標的字串 (格式: X,Y,Width,Height)
var coords = data[section][buttonKey].Split(',');
// 解析座標字串並回傳 (X, Y, Width, Height) 元組
return (int.Parse(coords[0]), int.Parse(coords[1]), int.Parse(coords[2]), int.Parse(coords[3]));
}
/// <summary>
/// 從 INI 設定數據中載入按鈕的圖片資料 (包含正常、點擊、滑鼠移過圖片)。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <param name="section">設定檔中按鈕圖片設定所屬的區塊名稱。</param>
/// <returns>回傳包含按鈕三種狀態圖片路徑的元組 (normal, mouseDown, mouseOver)。</returns>
private (string normal, string mouseDown, string mouseOver) LoadButtonImages(IniData data, string section)
{
// 讀取按鈕三種狀態的圖片路徑
return (
data[section]["normal"], // 正常狀態圖片路徑
data[section]["mouseDown"], // 點擊狀態圖片路徑
data[section]["mouseOver"] // 滑鼠移過狀態圖片路徑
);
}
/// <summary>
/// 初始化並設置語音按鈕的相關資料,包括符號、座標與圖片等。
/// </summary>
private void InitializePhoneticButtons()
{
// 載入配置資料
var data = LoadConfigData();
// 載入語音符號(如拼音、注音符號等)
phoneticSymbols = LoadPhoneticSymbols(data);
// 載入按鈕座標資料
phoneticButtonCoords = LoadButtonCoordinates(data, "PhoneticButtonCoordinates", 35);
// 載入按鈕圖片資料
phoneticButtonImages = LoadButtonImages(data, "PhoneticButtonImages", 35);
// 初始化語音按鈕陣列,總共有 35 個按鈕
phoneticButtonsForSingers = new Button[35];
// 設置每個語音按鈕
for (int i = 0; i < 35; i++)
{
// 根據按鈕索引讀取其圖片資料
var buttonImages = phoneticButtonImages[$"button{i}"];
// 創建並初始化語音按鈕,設定其背景圖片
CreatePhoneticButton(i, buttonImages.normal, buttonImages.mouseDown, buttonImages.mouseOver);
}
}
/// <summary>
/// 創建一個語音按鈕,並為其設置圖片、座標、事件等屬性。
/// </summary>
/// <param name="index">按鈕的索引,用來獲取對應的語音符號、座標和圖片資料。</param>
/// <param name="normalImagePath">正常狀態下的圖片路徑。</param>
/// <param name="mouseDownImagePath">點擊狀態下的圖片路徑。</param>
/// <param name="mouseOverImagePath">滑鼠移過狀態下的圖片路徑。</param>
private void CreatePhoneticButton(int index, string normalImagePath, string mouseDownImagePath, string mouseOverImagePath)
{
try
{
// 創建語音按鈕並設置其屬性
phoneticButtonsForSingers[index] = new Button
{
Name = $"phoneticButton_{phoneticSymbols[index]}", // 按鈕名稱設為語音符號名稱
BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath)), // 設定背景圖片
BackgroundImageLayout = ImageLayout.Stretch, // 設定圖片拉伸樣式
FlatStyle = FlatStyle.Flat, // 設定為平面風格
FlatAppearance = { BorderSize = 0 } // 設定無邊框
};
// 調整按鈕大小並設置位置
ResizeAndPositionButton(phoneticButtonsForSingers[index], phoneticButtonCoords[index].X, phoneticButtonCoords[index].Y,
phoneticButtonCoords[index].Width, phoneticButtonCoords[index].Height);
// 從檔案中讀取正常、點擊和滑鼠懸停狀態的圖片
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));
// 設置滑鼠事件:點擊、進入、離開等,改變按鈕的背景圖片
phoneticButtonsForSingers[index].MouseDown += (s, e) => phoneticButtonsForSingers[index].BackgroundImage = mouseDownImage;
phoneticButtonsForSingers[index].MouseUp += (s, e) => phoneticButtonsForSingers[index].BackgroundImage = normalImage;
phoneticButtonsForSingers[index].MouseEnter += (s, e) => phoneticButtonsForSingers[index].BackgroundImage = mouseOverImage;
phoneticButtonsForSingers[index].MouseLeave += (s, e) => phoneticButtonsForSingers[index].BackgroundImage = normalImage;
// 設置點擊事件處理方法
phoneticButtonsForSingers[index].Click += PhoneticButton_Click;
// 設置按鈕的 Tag 屬性為對應的語音符號
phoneticButtonsForSingers[index].Tag = phoneticSymbols[index];
// 將按鈕添加到表單的控制項集合中
this.Controls.Add(phoneticButtonsForSingers[index]);
}
catch (Exception ex)
{
// 捕捉錯誤並輸出錯誤訊息
Console.WriteLine($"Error creating button at index {index}: {ex.Message}");
}
}
/// <summary>
/// 初始化所有與注音歌手相關的按鈕,包括語音符號按鈕、特殊按鈕及輸入框。
/// </summary>
private void InitializeButtonsForZhuYinSingers()
{
// 從配置檔案加載注音符號並初始化按鈕
LoadPhoneticSymbolsFromConfig();
// 初始化所有語音按鈕
InitializePhoneticButtons();
// 初始化注音歌手的特殊按鈕(例如音量、搜尋等)
InitializeSpecialButtonsForZhuYinSingers();
// 初始化注音歌手的輸入框
InitializeInputBoxZhuYinSingers();
}
/// <summary>
/// 移除圖像周圍的白色邊框,將邊框的像素透明化。
/// </summary>
/// <param name="imagePath">待處理的圖像文件路徑。</param>
/// <returns>處理後的圖像,其中白色邊框已被去除並替換為透明。</returns>
private Image RemoveWhiteBorder(string imagePath)
{
// 創建一個 Bitmap 物件來加載圖像
Bitmap bmp = new Bitmap(imagePath);
// 定義圖像的矩形區域,這是我們將要操作的區域
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
// 鎖定圖像的位圖數據,以便進行直接修改
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0; // 獲取位圖數據的起始位置
int bytes = Math.Abs(bmpData.Stride) * bmp.Height; // 計算圖像的總字節數
byte[] rgbValues = new byte[bytes]; // 用來存儲圖像的像素數據
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); // 從圖像數據中複製像素數據到 rgbValues 陣列
// 遍歷每個像素點,檢查是否為白色邊框
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x++)
{
int position = (y * bmpData.Stride) + (x * 4); // 計算當前像素的位址
byte b = rgbValues[position]; // 藍色分量
byte g = rgbValues[position + 1]; // 綠色分量
byte r = rgbValues[position + 2]; // 紅色分量
byte a = rgbValues[position + 3]; // alpha 分量(透明度)
// 如果當前像素在圖像邊緣且為白色 (255, 255, 255),則將其設為透明
if ((x < 5 || x > bmp.Width - 5 || y < 5 || y > bmp.Height - 5) && r == 255 && g == 255 && b == 255)
{
// 將白色像素的 RGB 設置為 255, 255, 255 且 alpha 設為 0 (透明)
rgbValues[position] = 255;
rgbValues[position + 1] = 255;
rgbValues[position + 2] = 255;
rgbValues[position + 3] = 0; // 透明
}
}
}
// 將修改後的像素數據重新複製回位圖數據
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// 解鎖圖像數據
bmp.UnlockBits(bmpData);
// 返回處理後的圖像
return bmp;
}
/// <summary>
/// 初始化與注音歌手相關的特殊按鈕,包括修改、清除和關閉按鈕。
/// </summary>
private void InitializeSpecialButtonsForZhuYinSingers()
{
// 初始化修改按鈕
InitializeModifyButtonZhuYinSingers();
// 初始化清除按鈕
InitializeClearButtonZhuYinSingers();
// 初始化關閉按鈕
InitializeCloseButtonZhuYinSingers();
}
/// <summary>
/// 初始化「修改」按鈕,設定按鈕的坐標、圖片和點擊事件。
/// </summary>
private void InitializeModifyButtonZhuYinSingers()
{
// 加載配置數據
var data = LoadConfigData();
// 讀取按鈕坐標
modifyButtonZhuYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "modifyButtonZhuYinSingers");
// 加載按鈕圖片(正常、鼠標懸停、鼠標按下)
var buttonImages = LoadButtonImages(data, "ModifyButtonImagesZhuYin");
// 創建「修改」按鈕,並設置坐標、圖片及點擊事件
modifyButtonZhuYinSingers = CreateSpecialButton(
"btnModifyZhuYinSingers", // 按鈕名稱
modifyButtonZhuYinCoords, // 按鈕坐標
buttonImages.normal, // 正常狀態圖片
buttonImages.mouseOver, // 鼠標懸停圖片
buttonImages.mouseDown, // 鼠標按下圖片
ModifyButtonZhuYinSingers_Click // 按鈕點擊事件
);
}
/// <summary>
/// 處理「修改」按鈕的點擊事件,該事件會刪除輸入框中的最後一個字符。
/// </summary>
/// <param name="sender">觸發事件的對象</param>
/// <param name="e">事件參數</param>
private void ModifyButtonZhuYinSingers_Click(object sender, EventArgs e)
{
// 如果輸入框不為空,且包含輸入內容,則刪除最後一個字符
if (this.Controls.Contains(inputBoxZhuYinSingers) && inputBoxZhuYinSingers.Text.Length > 0)
{
inputBoxZhuYinSingers.Text = inputBoxZhuYinSingers.Text.Substring(0, inputBoxZhuYinSingers.Text.Length - 1);
}
}
/// <summary>
/// 初始化「清除」按鈕,設定按鈕的坐標、圖片和點擊事件。
/// </summary>
private void InitializeClearButtonZhuYinSingers()
{
// 加載配置數據
var data = LoadConfigData();
// 讀取按鈕坐標
clearButtonZhuYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "clearButtonZhuYinSingers");
// 加載按鈕圖片(正常、鼠標懸停、鼠標按下)
var buttonImages = LoadButtonImages(data, "ClearButtonImagesZhuYin");
// 創建「清除」按鈕,並設置坐標、圖片及點擊事件
clearButtonZhuYinSingers = CreateSpecialButton(
"btnClearZhuYinSingers", // 按鈕名稱
clearButtonZhuYinCoords, // 按鈕坐標
buttonImages.normal, // 正常狀態圖片
buttonImages.mouseOver, // 鼠標懸停圖片
buttonImages.mouseDown, // 鼠標按下圖片
ClearButtonZhuYinSingers_Click // 按鈕點擊事件
);
}
/// <summary>
/// 處理「清除」按鈕的點擊事件,該事件會清空輸入框中的所有文本。
/// </summary>
/// <param name="sender">觸發事件的對象</param>
/// <param name="e">事件參數</param>
private void ClearButtonZhuYinSingers_Click(object sender, EventArgs e)
{
// 如果輸入框不為空,則清空該框的文本內容
if (this.Controls.Contains(inputBoxZhuYinSingers) && inputBoxZhuYinSingers.Text.Length > 0)
{
inputBoxZhuYinSingers.Text = "";
}
}
/// <summary>
/// 初始化「關閉」按鈕,設定按鈕的坐標、圖片和點擊事件。
/// </summary>
private void InitializeCloseButtonZhuYinSingers()
{
// 加載配置數據
var data = LoadConfigData();
// 讀取按鈕坐標
closeButtonZhuYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "closeButtonZhuYinSingers");
// 加載按鈕圖片(正常、鼠標懸停、鼠標按下)
var buttonImages = LoadButtonImages(data, "CloseButtonImagesZhuYin");
// 創建「關閉」按鈕,並設置坐標、圖片及點擊事件
closeButtonZhuYinSingers = CreateSpecialButton(
"btnCloseZhuYinSingers", // 按鈕名稱
closeButtonZhuYinCoords, // 按鈕坐標
buttonImages.normal, // 正常狀態圖片
buttonImages.mouseOver, // 鼠標懸停圖片
buttonImages.mouseDown, // 鼠標按下圖片
CloseButtonZhuYinSingers_Click // 按鈕點擊事件
);
}
/// <summary>
/// 「關閉」按鈕的點擊事件處理方法。
/// 隱藏 ZhuYin 歌手圖片框以及與其相關的按鈕。
/// </summary>
/// <param name="sender">觸發事件的對象,這裡是關閉按鈕。</param>
/// <param name="e">事件參數。</param>
private void CloseButtonZhuYinSingers_Click(object sender, EventArgs e)
{
// 隱藏 ZhuYin 歌手圖片框
pictureBoxZhuYinSingers.Visible = false;
// 關閉注音搜尋的按鈕顏色
zhuyinSearchButton.BackgroundImage = zhuyinSearchNormalBackground;
// 隱藏與 ZhuYin 歌手相關的所有按鈕
SetZhuYinSingersAndButtonsVisibility(false);
}
/// <summary>
/// 創建一個特殊的按鈕,並設定其顯示屬性、事件處理和位置。
/// </summary>
/// <param name="name">按鈕的名稱。</param>
/// <param name="coords">按鈕的坐標和大小,包含 X, Y, 寬度和高度。</param>
/// <param name="normalImagePath">按鈕正常狀態下的背景圖片路徑。</param>
/// <param name="mouseOverImagePath">鼠標懸停時按鈕的背景圖片路徑。</param>
/// <param name="mouseDownImagePath">鼠標點擊時按鈕的背景圖片路徑。</param>
/// <param name="clickEventHandler">按鈕的點擊事件處理程序。</param>
/// <returns>創建並返回的按鈕對象。</returns>
private Button CreateSpecialButton(string name, (int X, int Y, int Width, int Height) coords, string normalImagePath, string mouseOverImagePath, string mouseDownImagePath, EventHandler clickEventHandler)
{
// 創建按鈕並設定基本屬性
var button = new Button
{
Name = name,
FlatStyle = FlatStyle.Flat,
FlatAppearance = { BorderSize = 0, MouseDownBackColor = Color.Transparent, MouseOverBackColor = Color.Transparent },
BackgroundImageLayout = ImageLayout.Stretch,
BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath))
};
// 設定按鈕的大小和位置
ResizeAndPositionButton(button, coords.X, coords.Y, coords.Width, coords.Height);
// 設定鼠標事件:進入、離開、按下、放開
button.MouseEnter += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, mouseOverImagePath));
button.MouseLeave += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath));
button.MouseDown += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, mouseDownImagePath));
button.MouseUp += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath));
// 註冊點擊事件處理
button.Click += clickEventHandler;
// 將按鈕添加到控件集合中
this.Controls.Add(button);
return button;
}
/// <summary>
/// 初始化 ZhuYin 歌手的輸入框,並設定其屬性與事件處理程序。
/// </summary>
private void InitializeInputBoxZhuYinSingers()
{
try
{
// 加載輸入框配置
LoadInputBoxConfig();
// 創建一個 RichTextBox 控件來作為輸入框
inputBoxZhuYinSingers = new RichTextBox
{
Name = "inputBoxZhuYinSingers",
ForeColor = inputBoxForeColor, // 設定文字顏色
Font = new Font(inputBoxFontName, inputBoxFontSize, inputBoxFontStyle), // 設定字體樣式
ScrollBars = RichTextBoxScrollBars.None // 不顯示滾動條
};
// 調整和定位輸入框的位置及大小
ResizeAndPositionControl(inputBoxZhuYinSingers, inputBoxZhuYinCoords.X, inputBoxZhuYinCoords.Y, inputBoxZhuYinCoords.Width, inputBoxZhuYinCoords.Height);
// 設定文本變更事件,當輸入框內容改變時觸發
inputBoxZhuYinSingers.TextChanged += (sender, e) =>
{
string searchText = inputBoxZhuYinSingers.Text;
// 使用注音符號開頭的歌手名稱進行搜索
var searchResults = allArtists.Where(artist => artist.Phonetic.StartsWith(searchText)).ToList();
// 使用注音符號包含的歌手名稱進行搜索
currentPage = 0;
currentArtistList = searchResults;
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSingers(currentArtistList);
};
// 將輸入框加入到窗體的控件集合中
this.Controls.Add(inputBoxZhuYinSingers);
}
catch (Exception ex)
{
// 如果初始化過程中出現錯誤,則在控制台輸出錯誤信息
Console.WriteLine("Error initializing inputBoxZhuYinSingers: " + ex.Message);
}
}
/// <summary>
/// 從配置文件 `config.ini` 中加載輸入框的設置,包括位置、大小、字體等屬性。
/// </summary>
private void LoadInputBoxConfig()
{
try
{
// 創建 INI 解析器
var parser = new FileIniDataParser();
string iniFilePath = "config.ini"; // 配置文件的路徑
IniData data;
// 打開並讀取配置文件
using (var reader = new StreamReader(iniFilePath, Encoding.UTF8))
{
data = parser.ReadData(reader);
}
// 從配置中加載輸入框的坐標和大小
inputBoxZhuYinCoords = (
int.Parse(data["InputBoxZhuYinSingers"]["X"]), // 輸入框的 X 坐標
int.Parse(data["InputBoxZhuYinSingers"]["Y"]), // 輸入框的 Y 坐標
int.Parse(data["InputBoxZhuYinSingers"]["Width"]), // 輸入框的寬度
int.Parse(data["InputBoxZhuYinSingers"]["Height"]) // 輸入框的高度
);
// 從配置中加載字體屬性
inputBoxFontName = data["InputBoxZhuYinSingers"]["FontName"]; // 字體名稱
inputBoxFontSize = float.Parse(data["InputBoxZhuYinSingers"]["FontSize"]); // 字體大小
inputBoxFontStyle = (FontStyle)Enum.Parse(typeof(FontStyle), data["InputBoxZhuYinSingers"]["FontStyle"]); // 字體樣式
inputBoxForeColor = Color.FromName(data["InputBoxZhuYinSingers"]["ForeColor"]); // 字體顏色
}
catch (Exception ex)
{
// 若發生錯誤,顯示錯誤信息
Console.WriteLine("Error loading inputBox configuration: " + ex.Message);
}
}
/// <summary>
/// 存儲 `pictureBoxZhuYinSingers` 控制項的坐標和大小設置。
/// </summary>
/// <remarks>
/// 這個元組包含了 `X`、`Y` 坐標以及 `Width`、`Height` 大小,用於配置 `pictureBoxZhuYinSingers` 的位置和大小。
/// </remarks>
private (int X, int Y, int Width, int Height) pictureBoxZhuYinSingerCoords;
/// <summary>
/// 從配置檔案中讀取 `PictureBoxZhuYinSingers` 控制項的坐標和大小設置。
/// </summary>
private void LoadPictureBoxZhuYinSingerCoordsFromConfig()
{
var parser = new FileIniDataParser();
IniData data = parser.ReadFile("config.ini");
var coords = data["PictureBoxZhuYinSingers"];
pictureBoxZhuYinSingerCoords = (
int.Parse(coords["X"]),
int.Parse(coords["Y"]),
int.Parse(coords["Width"]),
int.Parse(coords["Height"])
);
}
/// <summary>
/// 顯示圖片並根據配置文件設置顯示區域的大小和位置。
/// </summary>
/// <param name="imagePath">圖片的路徑。</param>
private void ShowImageOnPictureBoxZhuYinSingers(string imagePath)
{
// 讀取配置文件中的顯示區域設置
LoadPictureBoxZhuYinSingerCoordsFromConfig();
// 加載原始圖片
Bitmap originalImage = new Bitmap(imagePath);
// 創建顯示區域,根據配置文件中的坐標和大小設置
Rectangle displayArea = new Rectangle(pictureBoxZhuYinSingerCoords.X, pictureBoxZhuYinSingerCoords.Y, pictureBoxZhuYinSingerCoords.Width, pictureBoxZhuYinSingerCoords.Height);
// 設置圖片到 PictureBox
pictureBoxZhuYinSingers.Image = originalImage;
// 調整 PictureBox 大小和位置
ResizeAndPositionPictureBox(pictureBoxZhuYinSingers, displayArea.X, displayArea.Y, displayArea.Width, displayArea.Height);
// 顯示圖片
pictureBoxZhuYinSingers.Visible = true;
}
/// <summary>
/// 設置注音歌手相關控制項(包括圖片框、按鈕和輸入框)的顯示或隱藏狀態。
/// </summary>
/// <param name="isVisible">指定控件是否可見。True 為顯示False 為隱藏。</param>
private void SetZhuYinSingersAndButtonsVisibility(bool isVisible)
{
// 定義一個動作來處理控制項的顯示或隱藏
System.Action action = () =>
{
try
{
// 暫停控制項佈局的重新排版,提高效率
SuspendLayout();
// 檢查並設置圖片框的可見性
if (pictureBoxZhuYinSingers == null)
{
Console.WriteLine("pictureBoxZhuYinSingers is null");
}
else
{
pictureBoxZhuYinSingers.Visible = isVisible;
if (isVisible) pictureBoxZhuYinSingers.BringToFront(); // 如果顯示,將其置於最前
}
// 檢查並設置拼音按鈕的可見性
if (phoneticButtonsForSingers == null)
{
Console.WriteLine("phoneticButtonsForSingers is null");
}
else
{
foreach (var button in phoneticButtonsForSingers)
{
if (button == null)
{
Console.WriteLine("One of the phoneticButtonsForSingers is null");
}
else
{
button.Visible = isVisible;
if (isVisible) button.BringToFront(); // 如果顯示,將其置於最前
}
}
}
// 檢查並設置修改按鈕的可見性
if (modifyButtonZhuYinSingers == null)
{
Console.WriteLine("modifyButtonZhuYinSingers is null");
}
else
{
modifyButtonZhuYinSingers.Visible = isVisible;
if (isVisible) modifyButtonZhuYinSingers.BringToFront(); // 如果顯示,將其置於最前
}
// 檢查並設置清除按鈕的可見性
if (clearButtonZhuYinSingers == null)
{
Console.WriteLine("clearButtonZhuYinSingers is null");
}
else
{
clearButtonZhuYinSingers.Visible = isVisible;
if (isVisible) clearButtonZhuYinSingers.BringToFront(); // 如果顯示,將其置於最前
}
// 檢查並設置關閉按鈕的可見性
if (closeButtonZhuYinSingers == null)
{
Console.WriteLine("closeButtonZhuYinSingers is null");
}
else
{
closeButtonZhuYinSingers.Visible = isVisible;
if (isVisible) closeButtonZhuYinSingers.BringToFront(); // 如果顯示,將其置於最前
}
// 檢查並設置輸入框的可見性
if (inputBoxZhuYinSingers == null)
{
Console.WriteLine("inputBoxZhuYinSingers is null");
}
else
{
inputBoxZhuYinSingers.Visible = isVisible;
if (isVisible) inputBoxZhuYinSingers.BringToFront(); // 如果顯示,將其置於最前
}
// 恢復控制項的佈局重新排版
ResumeLayout();
PerformLayout();
// 刷新所有控制項的顯示
pictureBoxZhuYinSingers?.Refresh();
if (phoneticButtonsForSingers != null)
{
foreach (var button in phoneticButtonsForSingers)
{
button?.Refresh(); // 刷新每個按鈕
}
}
modifyButtonZhuYinSingers?.Refresh();
clearButtonZhuYinSingers?.Refresh();
closeButtonZhuYinSingers?.Refresh();
inputBoxZhuYinSingers?.Refresh();
}
catch (Exception ex)
{
Console.WriteLine("Error in SetZhuYinSingersAndButtonsVisibility: " + ex.Message);
}
};
// 檢查是否需要在主執行緒外執行
if (this.InvokeRequired)
{
this.Invoke(action); // 如果需要,透過主執行緒執行
}
else
{
action(); // 否則直接執行
}
}
}
}

View File

@ -0,0 +1,439 @@
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;
using IniParser;
using IniParser.Model;
using System.Text;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private PictureBox pictureBoxEnglishSingers;
private Button[] numberButtonsForSingers;
private Button[] letterButtonsForEnglishSingers;
private Button modifyButtonEnglishSingers;
private Button clearButtonEnglishSingers;
private Button closeButtonEnglishSingers;
private (int X, int Y, int Width, int Height) modifyButtonEnglishCoords;
private (int X, int Y, int Width, int Height) clearButtonEnglishCoords;
private (int X, int Y, int Width, int Height) closeButtonEnglishCoords;
private RichTextBox inputBoxEnglishSingers;
private void EnglishSearchSingersButton_Click(object sender, EventArgs e)
{
zhuyinSearchButton.BackgroundImage = zhuyinSearchNormalBackground;
englishSearchButton.BackgroundImage = englishSearchActiveBackground;
pinyinSearchButton.BackgroundImage = pinyinSearchNormalBackground;
wordCountSearchButton.BackgroundImage = wordCountSearchNormalBackground;
handWritingSearchButton.BackgroundImage = handWritingSearchNormalBackground;
bool shouldBeVisible = !pictureBoxEnglishSingers.Visible;
var configData = LoadConfigData();
string imagePath = Path.Combine(Application.StartupPath, configData["ImagePaths"]["EnglishSingers"]);
ShowImageOnPictureBoxEnglishSingers(Path.Combine(Application.StartupPath, imagePath));
SetZhuYinSingersAndButtonsVisibility(false);
SetPinYinSingersAndButtonsVisibility(false);
SetHandWritingForSingersAndButtonsVisibility(false);
SetWordCountSingersAndButtonsVisibility(false); // 隱藏字數搜尋相關控件
SetEnglishSingersAndButtonsVisibility(true);
//SetPictureBoxArtistSearchAndButtonsVisibility(false);
ResetinputBox();
pictureBoxEnglishSingers.Visible = true;
}
private (int X, int Y, int Width, int Height)[] numberButtonCoords;
private void LoadNumberButtonCoordsFromConfig()
{
var parser = new FileIniDataParser();
IniData data = parser.ReadFile("config.ini");
var buttonList = new List<(int X, int Y, int Width, int Height)>();
for (int i = 1; i <= 10; i++)
{
var coordString = data["NumberButtonCoordinates"][$"button{i}"];
var coords = coordString.Split(',');
buttonList.Add((int.Parse(coords[0]), int.Parse(coords[1]), int.Parse(coords[2]), int.Parse(coords[3])));
}
numberButtonCoords = buttonList.ToArray();
}
private Button CreateButton(string name, (int X, int Y, int Width, int Height) coords, string normalImagePath, string mouseDownImagePath, string mouseOverImagePath, EventHandler clickEventHandler)
{
var button = new Button
{
Name = name,
FlatStyle = FlatStyle.Flat,
FlatAppearance = { BorderSize = 0, MouseDownBackColor = Color.Transparent, MouseOverBackColor = Color.Transparent },
BackgroundImageLayout = ImageLayout.Stretch,
BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath))
};
ResizeAndPositionButton(button, coords.X, coords.Y, coords.Width, coords.Height);
button.MouseEnter += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, mouseOverImagePath));
button.MouseLeave += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath));
button.MouseDown += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, mouseDownImagePath));
button.MouseUp += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath));
button.Click += clickEventHandler;
return button;
}
private void InitializeNumberButtonsForSingers()
{
var data = LoadConfigData();
numberButtonCoords = LoadButtonCoordinates(data, "NumberButtonCoordinates", 10);
var buttonImages = LoadButtonImages(data, "NumberButtonImages", 10);
numberButtonsForSingers = new Button[10];
for (int i = 0; i < 10; i++)
{
string normalImagePath = buttonImages[$"button{i}"].normal;
string mouseDownImagePath = buttonImages[$"button{i}"].mouseDown;
string mouseOverImagePath = buttonImages[$"button{i}"].mouseOver;
if (normalImagePath == null || mouseDownImagePath == null || mouseOverImagePath == null)
{
Console.WriteLine($"Error: One or more image paths for button{i} are null.");
continue;
}
numberButtonsForSingers[i] = CreateButton(
$"numberButton_{i}",
numberButtonCoords[i],
normalImagePath,
mouseDownImagePath,
mouseOverImagePath,
NumberButtonForSingers_Click
);
numberButtonsForSingers[i].Tag = (i + 1) % 10;
this.Controls.Add(numberButtonsForSingers[i]);
}
}
private void NumberButtonForSingers_Click(object sender, EventArgs e)
{
var button = sender as Button;
if (button != null && button.Tag != null)
{
if (inputBoxEnglishSingers.Visible)
{
inputBoxEnglishSingers.Text += button.Tag.ToString();
}
}
}
private void InitializeLetterButtonsForEnglishSingers()
{
var data = LoadConfigData();
var buttonImages = LoadButtonImages(data, "EnglishLetterButtonImages", 26);
string qwertyLayout = "QWERTYUIOPASDFGHJKLZXCVBNM";
letterButtonsForEnglishSingers = new Button[26];
for (int i = 0; i < 26; i++)
{
var coords = data["EnglishLetterButtonCoordinates"][$"button{i}"].Split(',');
letterButtonsForEnglishSingers[i] = CreateButton(
$"letterButton_{qwertyLayout[i]}",
(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,
LetterButtonEnglishSingers_Click
);
letterButtonsForEnglishSingers[i].Tag = qwertyLayout[i];
this.Controls.Add(letterButtonsForEnglishSingers[i]);
}
}
private void LetterButtonEnglishSingers_Click(object sender, EventArgs e)
{
var button = sender as Button;
if (button != null && button.Tag != null)
{
if (inputBoxEnglishSingers.Visible)
{
inputBoxEnglishSingers.Text += button.Tag.ToString();
}
}
}
private void InitializeButtonsForEnglishSingers()
{
InitializeNumberButtonsForSingers();
InitializeLetterButtonsForEnglishSingers();
InitializeModifyButtonEnglishSingers();
InitializeClearButtonEnglishSingers();
InitializeCloseButtonEnglishSingers();
InitializeInputBoxEnglishSingers();
}
private void InitializeModifyButtonEnglishSingers()
{
var data = LoadConfigData();
modifyButtonEnglishCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "modifyButtonEnglishSingers");
var buttonImages = LoadButtonImages(data, "ModifyButtonImagesEnglish");
modifyButtonEnglishSingers = CreateSpecialButton(
"btnModifyEnglishSingers",
modifyButtonEnglishCoords,
buttonImages.normal,
buttonImages.mouseOver,
buttonImages.mouseDown,
ModifyButtonEnglishSingers_Click
);
this.Controls.Add(modifyButtonEnglishSingers);
}
private void ModifyButtonEnglishSingers_Click(object sender, EventArgs e)
{
if (this.Controls.Contains(inputBoxEnglishSingers) && inputBoxEnglishSingers.Text.Length > 0)
{
inputBoxEnglishSingers.Text = inputBoxEnglishSingers.Text.Substring(0, inputBoxEnglishSingers.Text.Length - 1);
}
}
private void InitializeClearButtonEnglishSingers()
{
var data = LoadConfigData();
clearButtonEnglishCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "clearButtonEnglishSingers");
var buttonImages = LoadButtonImages(data, "ClearButtonImagesEnglish");
clearButtonEnglishSingers = CreateSpecialButton(
"btnClearEnglishSingers",
clearButtonEnglishCoords,
buttonImages.normal,
buttonImages.mouseOver,
buttonImages.mouseDown,
ClearButtonEnglishSingers_Click
);
this.Controls.Add(clearButtonEnglishSingers);
}
private void ClearButtonEnglishSingers_Click(object sender, EventArgs e)
{
if (this.Controls.Contains(inputBoxEnglishSingers) && inputBoxEnglishSingers.Text.Length > 0)
{
inputBoxEnglishSingers.Text = "";
}
}
private void InitializeCloseButtonEnglishSingers()
{
var data = LoadConfigData();
closeButtonEnglishCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "closeButtonEnglishSingers");
var buttonImages = LoadButtonImages(data, "CloseButtonImagesEnglish");
closeButtonEnglishSingers = CreateSpecialButton(
"btnCloseEnglishSingers",
closeButtonEnglishCoords,
buttonImages.normal,
buttonImages.mouseOver,
buttonImages.mouseDown,
CloseButtonEnglishSingers_Click
);
this.Controls.Add(closeButtonEnglishSingers);
}
private void CloseButtonEnglishSingers_Click(object sender, EventArgs e)
{
pictureBoxEnglishSingers.Visible = false;
englishSearchButton.BackgroundImage = englishSearchNormalBackground;
SetEnglishSingersAndButtonsVisibility(false);
}
private void InitializeInputBoxEnglishSingers()
{
try
{
var parser = new FileIniDataParser();
parser.Parser.Configuration.AssigmentSpacer = "";
parser.Parser.Configuration.CommentString = "#";
parser.Parser.Configuration.CaseInsensitive = true;
IniData data;
using (var reader = new StreamReader("config.ini", System.Text.Encoding.UTF8))
{
data = parser.ReadData(reader);
}
int x = int.Parse(data["InputBoxEnglishSingers"]["X"]);
int y = int.Parse(data["InputBoxEnglishSingers"]["Y"]);
int width = int.Parse(data["InputBoxEnglishSingers"]["Width"]);
int height = int.Parse(data["InputBoxEnglishSingers"]["Height"]);
string fontName = data["InputBoxEnglishSingers"]["FontName"];
float fontSize = float.Parse(data["InputBoxEnglishSingers"]["FontSize"]);
FontStyle fontStyle = (FontStyle)Enum.Parse(typeof(FontStyle), data["InputBoxEnglishSingers"]["FontStyle"]);
Color foreColor = Color.FromName(data["InputBoxEnglishSingers"]["ForeColor"]);
inputBoxEnglishSingers = new RichTextBox
{
Visible = false,
Name = "inputBoxEnglishSingers",
ForeColor = foreColor,
Font = new Font(fontName, fontSize / 900 * Screen.PrimaryScreen.Bounds.Height, fontStyle)
};
ResizeAndPositionControl(inputBoxEnglishSingers, x, y, width, height);
inputBoxEnglishSingers.TextChanged += (sender, e) =>
{
string searchText = inputBoxEnglishSingers.Text;
var searchResults = allArtists.Where(artist => artist.Name.Replace(" ", "").StartsWith(searchText)).ToList();
currentPage = 0;
currentArtistList = searchResults;
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSingers(currentArtistList);
};
this.Controls.Add(inputBoxEnglishSingers);
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
private void ShowImageOnPictureBoxEnglishSingers(string imagePath)
{
try
{
var parser = new FileIniDataParser();
parser.Parser.Configuration.AssigmentSpacer = "";
parser.Parser.Configuration.CommentString = "#";
parser.Parser.Configuration.CaseInsensitive = true;
IniData data;
using (var reader = new StreamReader("config.ini", System.Text.Encoding.UTF8))
{
data = parser.ReadData(reader);
}
int x = int.Parse(data["PictureBoxEnglishSingers"]["X"]);
int y = int.Parse(data["PictureBoxEnglishSingers"]["Y"]);
int width = int.Parse(data["PictureBoxEnglishSingers"]["Width"]);
int height = int.Parse(data["PictureBoxEnglishSingers"]["Height"]);
Bitmap originalImage = new Bitmap(imagePath);
pictureBoxEnglishSingers.Image = originalImage;
ResizeAndPositionPictureBox(pictureBoxEnglishSingers, x, y, width, height);
pictureBoxEnglishSingers.Visible = true;
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
private void SetEnglishSingersAndButtonsVisibility(bool isVisible)
{
System.Action action = () =>
{
SuspendLayout();
pictureBoxEnglishSingers.Visible = isVisible;
if (isVisible) pictureBoxEnglishSingers.BringToFront();
foreach (var button in numberButtonsForSingers)
{
button.Visible = isVisible;
if (isVisible) button.BringToFront();
}
foreach (var button in letterButtonsForEnglishSingers)
{
button.Visible = isVisible;
if (isVisible) button.BringToFront();
}
if (modifyButtonEnglishSingers != null)
{
modifyButtonEnglishSingers.Visible = isVisible;
if (isVisible) modifyButtonEnglishSingers.BringToFront();
}
if (clearButtonEnglishSingers != null)
{
clearButtonEnglishSingers.Visible = isVisible;
if (isVisible) clearButtonEnglishSingers.BringToFront();
}
closeButtonEnglishSingers.Visible = isVisible;
if (isVisible) closeButtonEnglishSingers.BringToFront();
inputBoxEnglishSingers.Visible = isVisible;
if (isVisible) inputBoxEnglishSingers.BringToFront();
ResumeLayout();
PerformLayout();
pictureBoxEnglishSingers.Refresh();
foreach (var button in numberButtonsForSingers.Concat(letterButtonsForEnglishSingers))
{
button.Refresh();
}
modifyButtonEnglishSingers.Refresh();
clearButtonEnglishSingers.Refresh();
closeButtonEnglishSingers.Refresh();
inputBoxEnglishSingers.Refresh();
};
if (this.InvokeRequired)
{
this.Invoke(action);
}
else
{
action();
}
}
}
}

View File

@ -0,0 +1,308 @@
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using Microsoft.Ink;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private PictureBox pictureBoxHandWritingSingers;
private Button refillButtonHandWritingSingers;
private Button clearButtonHandWritingSingers;
private Button closeButtonForSingers;
private (int X, int Y, int Width, int Height) refillButtonHandWritingCoords;
private (int X, int Y, int Width, int Height) clearButtonHandWritingCoords;
private (int X, int Y, int Width, int Height) closeButtonHandWritingCoords;
private void HandWritingSearchButtonForSingers_Click(object sender, EventArgs e)
{
this.SuspendLayout();
zhuyinSearchButton.BackgroundImage = zhuyinSearchNormalBackground;
englishSearchButton.BackgroundImage = englishSearchNormalBackground;
pinyinSearchButton.BackgroundImage = pinyinSearchNormalBackground;
wordCountSearchButton.BackgroundImage = wordCountSearchNormalBackground;
handWritingSearchButton.BackgroundImage = handWritingSearchActiveBackground;
EnableDoubleBuffering(handWritingPanelForSingers);
EnableDoubleBuffering(handwritingInputBoxForSingers);
EnableDoubleBuffering(candidateListBoxForSingers);
EnableDoubleBuffering(pictureBoxHandWritingSingers);
EnableDoubleBuffering(refillButtonHandWritingSingers);
EnableDoubleBuffering(closeButtonForSingers);
var configData = LoadConfigData();
string handWritingImagePath = Path.Combine(Application.StartupPath, configData["ImagePaths"]["HandWritingSingers"]);
ShowImageOnPictureBoxHandWritingSingers(Path.Combine(Application.StartupPath, handWritingImagePath));
SetZhuYinSingersAndButtonsVisibility(false);
SetEnglishSingersAndButtonsVisibility(false);
SetPinYinSingersAndButtonsVisibility(false);
SetWordCountSingersAndButtonsVisibility(false); // 隱藏字數搜尋相關控件
//SetPictureBoxArtistSearchAndButtonsVisibility(false);
SetHandWritingForSingersAndButtonsVisibility(true);
ResetinputBox();
this.ResumeLayout();
}
private Panel handWritingPanelForSingers;
private InkOverlay inkOverlayForSingers;
private RichTextBox handwritingInputBoxForSingers;
private ListBox candidateListBoxForSingers;
private void InitializeHandWritingForSingers()
{
InitializeHandWritingPanelForSingers();
InitializeInkOverlayForSingers();
InitializeHandwritingInputBoxForSingers();
InitializeCandidateListBoxForSingers();
InitializeSpecialButtonsForHandWritingSingers();
}
private void InitializeHandWritingPanelForSingers()
{
handWritingPanelForSingers = new Panel
{
BorderStyle = BorderStyle.FixedSingle,
Visible = false
};
ResizeAndPositionControl(handWritingPanelForSingers, 366, 448, 650, 260);
this.Controls.Add(handWritingPanelForSingers);
}
private void InitializeInkOverlayForSingers()
{
try
{
inkOverlayForSingers = new InkOverlay(handWritingPanelForSingers);
inkOverlayForSingers.Enabled = false;
inkOverlayForSingers.Ink = new Ink();
inkOverlayForSingers.DefaultDrawingAttributes.Color = Color.Black;
inkOverlayForSingers.DefaultDrawingAttributes.Width = 100;
inkOverlayForSingers.Stroke += new InkCollectorStrokeEventHandler(InkOverlayForSingers_Stroke);
inkOverlayForSingers.Enabled = true;
}
catch (Exception ex)
{
Console.WriteLine("Failed to initialize ink overlay for singers: " + ex.Message);
}
}
private void InkOverlayForSingers_Stroke(object sender, InkCollectorStrokeEventArgs e)
{
RecognizeInk(inkOverlayForSingers, candidateListBoxForSingers);
}
private void InitializeHandwritingInputBoxForSingers()
{
handwritingInputBoxForSingers = new RichTextBox
{
Font = new Font("微軟正黑體", (float)26 / 900 * Screen.PrimaryScreen.Bounds.Height, FontStyle.Regular),
Visible = false
};
ResizeAndPositionControl(handwritingInputBoxForSingers, 366, 373, 541, 62);
this.Controls.Add(handwritingInputBoxForSingers);
handwritingInputBoxForSingers.TextChanged += (sender, e) =>
{
string searchText = handwritingInputBoxForSingers.Text;
var searchResults = allArtists.Where(artist => artist.Name.StartsWith(searchText)).ToList();
currentPage = 0;
currentArtistList = searchResults;
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSingers(currentArtistList);
};
}
private void InitializeCandidateListBoxForSingers()
{
candidateListBoxForSingers = new ListBox
{
Font = new Font("微軟正黑體", (float)26 / 900 * Screen.PrimaryScreen.Bounds.Height, FontStyle.Regular),
Visible = false
};
ResizeAndPositionControl(candidateListBoxForSingers, 350 + 679, 448, 115, 260);
candidateListBoxForSingers.SelectedIndexChanged += CandidateListBoxForSingers_SelectedIndexChanged;
this.Controls.Add(candidateListBoxForSingers);
}
private void CandidateListBoxForSingers_SelectedIndexChanged(object sender, EventArgs e)
{
if (candidateListBoxForSingers.SelectedIndex != -1)
{
string selectedWord = candidateListBoxForSingers.SelectedItem.ToString();
handwritingInputBoxForSingers.Text += selectedWord;
candidateListBoxForSingers.Visible = false;
if (inkOverlayForSingers != null)
{
inkOverlayForSingers.Ink.DeleteStrokes();
handWritingPanelForSingers.Invalidate();
}
}
}
private void ShowImageOnPictureBoxHandWritingSingers(string imagePath)
{
Bitmap originalImage = new Bitmap(imagePath);
Rectangle displayArea = new Rectangle(350, 360, 810, 360);
pictureBoxHandWritingSingers.Image = originalImage;
ResizeAndPositionPictureBox(pictureBoxHandWritingSingers, displayArea.X, displayArea.Y, displayArea.Width, displayArea.Height);
pictureBoxHandWritingSingers.Visible = true;
}
private void SetHandWritingForSingersAndButtonsVisibility(bool isVisible)
{
EnableDoubleBuffering(handWritingPanelForSingers);
EnableDoubleBuffering(handwritingInputBoxForSingers);
EnableDoubleBuffering(candidateListBoxForSingers);
EnableDoubleBuffering(pictureBoxHandWritingSingers);
EnableDoubleBuffering(refillButtonHandWritingSingers);
EnableDoubleBuffering(clearButtonHandWritingSingers);
EnableDoubleBuffering(closeButtonForSingers);
handWritingPanelForSingers.Visible = isVisible;
handwritingInputBoxForSingers.Visible = isVisible;
inkOverlayForSingers.Enabled = isVisible;
candidateListBoxForSingers.Visible = isVisible;
pictureBoxHandWritingSingers.Visible = isVisible;
refillButtonHandWritingSingers.Visible = isVisible;
clearButtonHandWritingSingers.Visible = isVisible;
closeButtonForSingers.Visible = isVisible;
if (isVisible)
{
pictureBoxHandWritingSingers.BringToFront();
handWritingPanelForSingers.BringToFront();
handwritingInputBoxForSingers.BringToFront();
candidateListBoxForSingers.BringToFront();
refillButtonHandWritingSingers.BringToFront();
clearButtonHandWritingSingers.BringToFront();
closeButtonForSingers.BringToFront();
}
}
private void InitializeSpecialButtonsForHandWritingSingers()
{
InitializeRefillButtonHandwritingSingers();
InitializeClearButtonHandWritingSingers();
InitializeCloseButtonForSingers();
}
private void InitializeRefillButtonHandwritingSingers()
{
var data = LoadConfigData();
refillButtonHandWritingCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "refillButtonHandWritingSingers");
var buttonImages = LoadButtonImages(data, "RefillButtonImagesHandWriting");
refillButtonHandWritingSingers = CreateSpecialButton(
"refillButtonHandWritingSingers",
refillButtonHandWritingCoords,
buttonImages.normal,
buttonImages.mouseOver,
buttonImages.mouseDown,
RefillButtonHandWritingSingers_Click
);
}
private void RefillButtonHandWritingSingers_Click(object sender, EventArgs e)
{
handwritingInputBoxForSingers.Text = "";
}
private void InitializeClearButtonHandWritingSingers()
{
var data = LoadConfigData();
clearButtonHandWritingCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "clearButtonHandWritingSingers");
var buttonImages = LoadButtonImages(data, "ClearButtonImagesHandWriting");
clearButtonHandWritingSingers = CreateSpecialButton(
"clearButtonHandWritingSingers",
clearButtonHandWritingCoords,
buttonImages.normal,
buttonImages.mouseOver,
buttonImages.mouseDown,
ClearButtonHandWritingSingers_Click
);
}
private void ClearButtonHandWritingSingers_Click(object sender, EventArgs e)
{
if (this.Controls.Contains(handWritingPanelForSingers) && inkOverlayForSingers != null)
{
inkOverlayForSingers.Ink.DeleteStrokes();
handWritingPanelForSingers.Invalidate();
}
}
private void InitializeCloseButtonForSingers()
{
var data = LoadConfigData();
closeButtonHandWritingCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "closeButtonForSingers");
var buttonImages = LoadButtonImages(data, "CloseButtonImagesHandWriting");
closeButtonForSingers = CreateSpecialButton(
"closeButtonForSingers",
closeButtonHandWritingCoords,
buttonImages.normal,
buttonImages.mouseOver,
buttonImages.mouseDown,
CloseButtonForSingers_Click
);
}
private void CloseButtonForSingers_Click(object sender, EventArgs e)
{
this.SuspendLayout();
SetHandWritingForSingersAndButtonsVisibility(false);
handWritingSearchButton.BackgroundImage = handWritingSearchNormalBackground;
this.ResumeLayout();
}
}
}

View File

@ -0,0 +1,379 @@
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
{
private PictureBox pictureBoxPinYinSingers;
private Button[] letterButtonsForPinYinSingers;
private Button modifyButtonPinYinSingers;
private Button clearButtonPinYinSingers;
private Button closeButtonPinYinSingers;
private (int X, int Y, int Width, int Height) modifyButtonPinYinCoords;
private (int X, int Y, int Width, int Height) clearButtonPinYinCoords;
private (int X, int Y, int Width, int Height) closeButtonPinYinCoords;
private RichTextBox inputBoxPinYinSingers;
private void PinyinSingerSearchButton_Click(object sender, EventArgs e)
{
zhuyinSearchButton.BackgroundImage = zhuyinSearchNormalBackground;
englishSearchButton.BackgroundImage = englishSearchNormalBackground;
pinyinSearchButton.BackgroundImage = pinyinSearchActiveBackground;
wordCountSearchButton.BackgroundImage = wordCountSearchNormalBackground;
handWritingSearchButton.BackgroundImage = handWritingSearchNormalBackground;
var configData = LoadConfigData();
string pinyinImagePath = Path.Combine(Application.StartupPath, configData["ImagePaths"]["PinYinSingers"]);
ShowImageOnPictureBoxPinYinSingers(Path.Combine(Application.StartupPath, pinyinImagePath));
SetZhuYinSingersAndButtonsVisibility(false); // 隱藏注音搜尋相關控件
SetEnglishSingersAndButtonsVisibility(false); // 隱藏英文搜尋相關控件
SetWordCountSingersAndButtonsVisibility(false); // 隱藏字數搜尋相關控件
SetHandWritingForSingersAndButtonsVisibility(false); // 隱藏手寫搜尋相關控件
SetPinYinSingersAndButtonsVisibility(true);
//SetPictureBoxArtistSearchAndButtonsVisibility(false);
pictureBoxPinYinSingers.Visible = true;
}
private void InitializeLetterButtonsForPinYinSingers()
{
var data = LoadConfigData();
var buttonImages = LoadButtonImages(data, "PinYinLetterButtonImages", 26);
string qwertyLayout = "QWERTYUIOPASDFGHJKLZXCVBNM";
letterButtonsForPinYinSingers = new Button[26];
for (int i = 0; i < 26; i++)
{
var coords = data["PinYinLetterButtonCoordinates"][$"button{i}"].Split(',');
letterButtonsForPinYinSingers[i] = CreateButton(
$"letterButton_{qwertyLayout[i]}",
(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,
LetterButtonPinYinSingers_Click
);
letterButtonsForPinYinSingers[i].Tag = qwertyLayout[i];
this.Controls.Add(letterButtonsForPinYinSingers[i]);
}
}
private void LetterButtonPinYinSingers_Click(object sender, EventArgs e)
{
var button = sender as Button;
if (button != null && button.Tag != null)
{
if (inputBoxPinYinSingers.Visible)
{
inputBoxPinYinSingers.Text += button.Tag.ToString();
}
}
}
private void InitializeButtonsForPinYinSingers()
{
InitializeLetterButtonsForPinYinSingers();
InitializeSpecialButtonsForPinYinSingers();
InitializeInputBoxPinYinSingers();
}
private void InitializeSpecialButtonsForPinYinSingers()
{
InitializeModifyButtonPinYinSingers();
InitializeClearButtonPinYinSingers();
InitializeCloseButtonPinYinSingers();
}
private void InitializeModifyButtonPinYinSingers()
{
var data = LoadConfigData();
modifyButtonPinYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "modifyButtonPinYinSingers");
var buttonImages = LoadButtonImages(data, "ModifyButtonImagesPinYin");
modifyButtonPinYinSingers = CreateSpecialButton(
"btnModifyPinYinSingers",
modifyButtonPinYinCoords,
buttonImages.normal,
buttonImages.mouseOver,
buttonImages.mouseDown,
ModifyButtonPinYinSingers_Click
);
}
private void ModifyButtonPinYinSingers_Click(object sender, EventArgs e)
{
if (this.Controls.Contains(inputBoxPinYinSingers) && inputBoxPinYinSingers.Text.Length > 0)
{
inputBoxPinYinSingers.Text = inputBoxPinYinSingers.Text.Substring(0, inputBoxPinYinSingers.Text.Length - 1);
}
}
private void InitializeClearButtonPinYinSingers()
{
var data = LoadConfigData();
clearButtonPinYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "clearButtonPinYinSingers");
var buttonImages = LoadButtonImages(data, "ClearButtonImagesPinYin");
clearButtonPinYinSingers = CreateSpecialButton(
"btnClearPinYinSingers",
clearButtonPinYinCoords,
buttonImages.normal,
buttonImages.mouseOver,
buttonImages.mouseDown,
ClearButtonPinYinSingers_Click
);
}
private void ClearButtonPinYinSingers_Click(object sender, EventArgs e)
{
if (this.Controls.Contains(inputBoxPinYinSingers) && inputBoxPinYinSingers.Text.Length > 0)
{
inputBoxPinYinSingers.Text = "";
}
}
private void InitializeCloseButtonPinYinSingers()
{
var data = LoadConfigData();
closeButtonPinYinCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "closeButtonPinYinSingers");
var buttonImages = LoadButtonImages(data, "CloseButtonImagesPinYin");
closeButtonPinYinSingers = CreateSpecialButton(
"btnClosePinYinSingers",
closeButtonPinYinCoords,
buttonImages.normal,
buttonImages.mouseOver,
buttonImages.mouseDown,
CloseButtonPinYinSingers_Click
);
}
private void CloseButtonPinYinSingers_Click(object sender, EventArgs e)
{
pictureBoxPinYinSingers.Visible = false;
pinyinSearchButton.BackgroundImage = pinyinSearchNormalBackground;
SetPinYinSingersAndButtonsVisibility(false);
}
private void InitializeInputBoxPinYinSingers()
{
try
{
var parser = new FileIniDataParser();
parser.Parser.Configuration.AssigmentSpacer = "";
parser.Parser.Configuration.CommentString = "#";
parser.Parser.Configuration.CaseInsensitive = true;
IniData data;
using (var reader = new StreamReader("config.ini", System.Text.Encoding.UTF8))
{
data = parser.ReadData(reader);
}
int x = int.Parse(data["InputBoxPinYinSingers"]["X"]);
int y = int.Parse(data["InputBoxPinYinSingers"]["Y"]);
int width = int.Parse(data["InputBoxPinYinSingers"]["Width"]);
int height = int.Parse(data["InputBoxPinYinSingers"]["Height"]);
string fontName = data["InputBoxPinYinSingers"]["FontName"];
float fontSize = float.Parse(data["InputBoxPinYinSingers"]["FontSize"]);
FontStyle fontStyle = (FontStyle)Enum.Parse(typeof(FontStyle), data["InputBoxPinYinSingers"]["FontStyle"]);
Color foreColor = Color.FromName(data["InputBoxPinYinSingers"]["ForeColor"]);
inputBoxPinYinSingers = new RichTextBox
{
Visible = false,
Name = "inputBoxPinYinSingers",
ForeColor = foreColor,
Font = new Font(fontName, fontSize / 900 * Screen.PrimaryScreen.Bounds.Height, fontStyle)
};
ResizeAndPositionControl(inputBoxPinYinSingers, x, y, width, height);
inputBoxPinYinSingers.TextChanged += (sender, e) =>
{
string searchText = inputBoxPinYinSingers.Text;
var searchResults = allSongs.Where(song => song.ArtistAPinyin.Replace(" ", "").StartsWith(searchText))
.Union(allSongs.Where(song => song.ArtistBPinyin.Replace(" ", "").StartsWith(searchText)))
.ToList();
currentPage = 0;
currentSongList = searchResults;
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
/////////////////////////////////////////////////////////////////
/*string searchText = inputBoxPinYinSingers.Text;
var searchResults = allArtists.Where(artist => artist.Name.Replace(" ", "").StartsWith(searchText)).ToList();
currentPage = 0;
currentArtistList = searchResults;
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSingers(currentArtistList);*/
};
this.Controls.Add(inputBoxPinYinSingers);
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
private (int X, int Y, int Width, int Height) pictureBoxPinYinSingerCoords;
private void LoadPictureBoxPinYinSingerCoordsFromConfig()
{
var parser = new FileIniDataParser();
IniData data = parser.ReadFile("config.ini");
var coords = data["PictureBoxPinYinSingers"];
pictureBoxPinYinSingerCoords = (
int.Parse(coords["X"]),
int.Parse(coords["Y"]),
int.Parse(coords["Width"]),
int.Parse(coords["Height"])
);
}
private void ShowImageOnPictureBoxPinYinSingers(string imagePath)
{
LoadPictureBoxPinYinSingerCoordsFromConfig();
Bitmap originalImage = new Bitmap(imagePath);
Rectangle displayArea = new Rectangle(pictureBoxPinYinSingerCoords.X, pictureBoxPinYinSingerCoords.Y, pictureBoxPinYinSingerCoords.Width, pictureBoxPinYinSingerCoords.Height);
pictureBoxPinYinSingers.Image = originalImage;
ResizeAndPositionPictureBox(pictureBoxPinYinSingers, displayArea.X, displayArea.Y, displayArea.Width, displayArea.Height);
pictureBoxPinYinSingers.Visible = true;
}
private void SetPinYinSingersAndButtonsVisibility(bool isVisible)
{
System.Action action = () =>
{
SuspendLayout();
if (pictureBoxPinYinSingers == null)
{
Console.WriteLine("pictureBoxPinYinSingers is null");
}
else
{
pictureBoxPinYinSingers.Visible = isVisible;
if (isVisible) pictureBoxPinYinSingers.BringToFront();
pictureBoxPinYinSingers.Refresh();
}
if (letterButtonsForPinYinSingers == null)
{
Console.WriteLine("letterButtonsForPinYinSingers is null");
}
else
{
foreach (var button in letterButtonsForPinYinSingers)
{
if (button == null)
{
Console.WriteLine("A button in letterButtonsForPinYinSingers is null");
}
else
{
button.Visible = isVisible;
if (isVisible) button.BringToFront();
button.Refresh();
}
}
}
if (modifyButtonPinYinSingers == null)
{
Console.WriteLine("modifyButtonPinYinSingers is null");
}
else
{
modifyButtonPinYinSingers.Visible = isVisible;
if (isVisible) modifyButtonPinYinSingers.BringToFront();
modifyButtonPinYinSingers.Refresh();
}
if (clearButtonPinYinSingers == null)
{
Console.WriteLine("clearButtonPinYinSingers is null");
}
else
{
clearButtonPinYinSingers.Visible = isVisible;
if (isVisible) clearButtonPinYinSingers.BringToFront();
clearButtonPinYinSingers.Refresh();
}
if (closeButtonPinYinSingers == null)
{
Console.WriteLine("closeButtonPinYinSingers is null");
}
else
{
closeButtonPinYinSingers.Visible = isVisible;
if (isVisible) closeButtonPinYinSingers.BringToFront();
closeButtonPinYinSingers.Refresh();
}
if (inputBoxPinYinSingers == null)
{
Console.WriteLine("inputBoxPinYinSingers is null");
}
else
{
inputBoxPinYinSingers.Visible = isVisible;
if (isVisible) inputBoxPinYinSingers.BringToFront();
inputBoxPinYinSingers.Refresh();
}
ResumeLayout();
PerformLayout();
};
if (this.InvokeRequired)
{
this.Invoke(action);
}
else
{
action();
}
}
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using Microsoft.Ink;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
}
}

View File

@ -0,0 +1,868 @@
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;
/*
WordCountSongs -> WordCountSingers
ZhuYin -> WordCount
phonetic -> numberWordCount
Phonetic -> NumberWordCount
inputBoxFontName -> inputBoxWordCountFontName
inputBoxFontSize -> inputBoxWordCountFontSize
inputBoxFontStyle -> inputBoxWordCountFontStyle
inputBoxForeColor -> inputBoxWordCountForeColor
LoadConfigData -> LoadConfigDataforWordCountSongs
LoadButtonCoordinates -> LoadButtonCoordinatesForWordCountSongs
LoadButtonImages -> LoadButtonImagesForWordCountSongs
LoadSpecialButtonCoordinates -> LoadSpecialButtonCoordinatesForWordCountSongs
RemoveWhiteBorder -> RemoveWhiteBorderForWordCountSongs
CreateSpecialButton -> CreateSpecialButtonForWordCountSongs
LoadInputBoxConfig -> LoadInputBoxConfigForWordCountSongs
*/
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private PictureBox pictureBoxWordCountSingers;
private Button[] numberWordCountButtonsForSingers;
private Button modifyButtonWordCountSingers;
private Button clearButtonWordCountSingers;
private Button closeButtonWordCountSingers;
private string[] numberWordCountSymbols;
private (int X, int Y, int Width, int Height)[] numberWordCountButtonCoords;
private Dictionary<string, (string normal, string mouseDown, string mouseOver)> numberWordCountButtonImages;
private (int X, int Y, int Width, int Height) modifyButtonWordCountCoords;
private (int X, int Y, int Width, int Height) clearButtonWordCountCoords;
private (int X, int Y, int Width, int Height) closeButtonWordCountCoords;
private RichTextBox inputBoxWordCountSingers;
private (int X, int Y, int Width, int Height) inputBoxWordCountCoords;
private string inputBoxWordCountFontName;
private float inputBoxWordCountFontSize;
private FontStyle inputBoxWordCountFontStyle;
private Color inputBoxWordCountForeColor;
/// <summary>
/// <para> 點擊「注音歌手搜尋」按鈕時執行的事件處理函式。</para>
/// <para>此函式負責更新按鈕的背景圖片、載入對應的歌手圖片,並切換相關 UI 控件的可見性。</para>
/// </summary>
/// <param name="sender">觸發事件的物件(通常是按鈕本身)。</param>
/// <param name="e">事件參數。</param>
private void WordCountSearchSingersButton_Click(object sender, EventArgs e)
{
// 設定按鈕背景,將「字數搜尋」設為啟動狀態,其餘按鈕恢復為正常狀態
wordCountSearchButton.BackgroundImage = wordCountSearchActiveBackground;
englishSearchButton.BackgroundImage = englishSearchNormalBackground;
pinyinSearchButton.BackgroundImage = pinyinSearchNormalBackground;
zhuyinSearchButton.BackgroundImage = zhuyinSearchNormalBackground;
handWritingSearchButton.BackgroundImage = handWritingSearchNormalBackground;
// 載入設定檔,取得圖片路徑資訊
var configData = LoadConfigDataforWordCountSingers();
// 取得「字數歌手圖片」的完整路徑
string imagePath = Path.Combine(Application.StartupPath, configData["ImagePaths"]["WordCountSingers"]);
// 在 PictureBox 中顯示對應的「字數歌手」圖片
ShowImageOnPictureBoxWordCountSingers(Path.Combine(Application.StartupPath, imagePath));
// 設定不同搜尋模式的 UI 控件可見性
SetZhuYinSingersAndButtonsVisibility(false); // 隱藏注音搜尋相關控件
SetEnglishSingersAndButtonsVisibility(false); // 隱藏英文字母搜尋相關控件
SetPinYinSingersAndButtonsVisibility(false); // 隱藏拼音搜尋相關控件
SetHandWritingForSingersAndButtonsVisibility(false); // 隱藏手寫搜尋相關控件
//SetPictureBoxArtistSearchAndButtonsVisibility(false); // 隱藏其他搜尋模式的圖片框
SetWordCountSingersAndButtonsVisibility(true); // 顯示字數搜尋相關控件
ResetinputBox();
// 顯示「字數歌手搜尋」的圖片框
pictureBoxWordCountSingers.Visible = true;
}
/// <summary>
/// <para>從 config.ini 設定檔中載入注音符號NumberWordCount Symbols。</para>
/// <para>讀取 ini 檔的 [NumberWordCountSymbols] 區塊並將「Symbols」欄位的值解析為陣列。</para>
/// </summary>
private void LoadNumberWordCountSymbolsFromConfig()
{
// 建立 INI 檔案解析器
var parser = new FileIniDataParser();
// 設定檔路徑
string iniFilePath = "config.ini";
IniData data;
// 以 UTF-8 編碼開啟並讀取 INI 檔案
using (var reader = new StreamReader(iniFilePath, Encoding.UTF8))
{
// 解析 INI 檔內容
data = parser.ReadData(reader);
}
// 取得 [NumberWordCountSymbols] 區塊中的 "Symbols" 欄位內容
string symbols = data["NumberWordCountSymbols"]["Symbols"];
// 將符號字串以逗號分隔,轉換為字串陣列
numberWordCountSymbols = symbols.Split(',');
}
/// <summary>
/// 從設定檔 (config.ini) 載入 INI 設定數據。
/// </summary>
/// <returns>回傳解析後的 INI 設定數據 (IniData)。</returns>
private IniData LoadConfigDataforWordCountSingers()
{
var parser = new FileIniDataParser();
string iniFilePath = "config.ini";
// 使用 UTF-8 讀取 INI 檔案並解析內容
using (var reader = new StreamReader(iniFilePath, Encoding.UTF8))
{
return parser.ReadData(reader);
}
}
/// <summary>
/// 從 INI 設定數據中讀取注音符號 (NumberWordCount Symbols)。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <returns>回傳包含注音符號的字串陣列。</returns>
private string[] LoadNumberWordCountSymbols(IniData data)
{
// 從 INI 檔案的 [NumberWordCountSymbols] 區塊取得 Symbols 欄位值
string symbols = data["NumberWordCountSymbols"]["Symbols"];
// 以逗號分隔字串並轉換為字串陣列
return symbols.Split(',');
}
/// <summary>
/// 從 INI 設定數據中載入按鈕座標資料。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <param name="section">設定檔中按鈕座標所屬的區塊名稱。</param>
/// <param name="buttonCount">按鈕數量。</param>
/// <returns>回傳包含按鈕座標的陣列,每個元素是由 (X, Y, Width, Height) 組成的元組。</returns>
private (int X, int Y, int Width, int Height)[] LoadButtonCoordinatesForWordCountSingers(IniData data, string section, int buttonCount)
{
var buttonList = new List<(int X, int Y, int Width, int Height)>();
// 迴圈讀取每個按鈕的座標設定
for (int i = 1; i <= buttonCount; i++)
{
// 取得按鈕座標的字串 (格式: X,Y,Width,Height)
var coordString = data[section][$"button{i}"];
var coords = coordString.Split(',');
// 將座標資料轉換為 (X, Y, Width, Height) 元組並加入清單
buttonList.Add((
int.Parse(coords[0]), // X 座標
int.Parse(coords[1]), // Y 座標
int.Parse(coords[2]), // 寬度
int.Parse(coords[3]) // 高度
));
}
// 回傳所有按鈕座標的陣列
return buttonList.ToArray();
}
/// <summary>
/// 從 INI 設定數據中載入按鈕圖片檔案路徑資料 (包含正常、點擊、滑鼠移過圖片)。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <param name="section">設定檔中按鈕圖片設定所屬的區塊名稱。</param>
/// <param name="buttonCount">按鈕數量。</param>
/// <returns>回傳一個字典,鍵是按鈕名稱,值是包含正常、點擊和滑鼠移過狀態的元組。</returns>
private Dictionary<string, (string normal, string mouseDown, string mouseOver)> LoadButtonImagesForWordCountSingers(IniData data, string section, int buttonCount)
{
var buttonImages = new Dictionary<string, (string normal, string mouseDown, string mouseOver)>();
// 迴圈讀取每個按鈕的圖片設定
for (int i = 0; i < 10; i++)
{
// 讀取按鈕的三種圖片狀態:正常、點擊、滑鼠移過
buttonImages[$"button{i}"] = (
data[section][$"button{i}_normal"], // 正常狀態圖片路徑
data[section][$"button{i}_mouseDown"], // 點擊狀態圖片路徑
data[section][$"button{i}_mouseOver"] // 滑鼠移過狀態圖片路徑
);
}
// 回傳包含所有按鈕圖片路徑資料的字典
return buttonImages;
}
/// <summary>
/// 從 INI 設定數據中載入特定按鈕的座標資料。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <param name="section">設定檔中按鈕座標所屬的區塊名稱。</param>
/// <param name="buttonKey">指定按鈕的鍵名 (如 "button1")。</param>
/// <returns>回傳包含按鈕座標的元組 (X, Y, Width, Height)。</returns>
private (int X, int Y, int Width, int Height) LoadSpecialButtonCoordinatesForWordCountSingers(IniData data, string section, string buttonKey)
{
// 取得按鈕座標的字串 (格式: X,Y,Width,Height)
var coords = data[section][buttonKey].Split(',');
// 解析座標字串並回傳 (X, Y, Width, Height) 元組
return (int.Parse(coords[0]), int.Parse(coords[1]), int.Parse(coords[2]), int.Parse(coords[3]));
}
/// <summary>
/// 從 INI 設定數據中載入按鈕的圖片資料 (包含正常、點擊、滑鼠移過圖片)。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <param name="section">設定檔中按鈕圖片設定所屬的區塊名稱。</param>
/// <returns>回傳包含按鈕三種狀態圖片路徑的元組 (normal, mouseDown, mouseOver)。</returns>
private (string normal, string mouseDown, string mouseOver) LoadButtonImagesForWordCountSingers(IniData data, string section)
{
// 讀取按鈕三種狀態的圖片路徑
return (
data[section]["normal"], // 正常狀態圖片路徑
data[section]["mouseDown"], // 點擊狀態圖片路徑
data[section]["mouseOver"] // 滑鼠移過狀態圖片路徑
);
}
/// <summary>
/// 初始化並設置語音按鈕的相關資料,包括符號、座標與圖片等。
/// </summary>
private void InitializeNumberWordCountButtons()
{
// 載入配置資料
var data = LoadConfigDataforWordCountSingers();
// 載入語音符號(如拼音、注音符號等)
numberWordCountSymbols = LoadNumberWordCountSymbols(data);
// 載入按鈕座標資料
numberWordCountButtonCoords = LoadButtonCoordinatesForWordCountSingers(data, "NumberWordCountButtonCoordinates", 10);
// 載入按鈕圖片資料
numberWordCountButtonImages = LoadButtonImagesForWordCountSingers(data, "NumberWordCountButtonImages", 10);
// 初始化語音按鈕陣列,總共有 10 個按鈕
numberWordCountButtonsForSingers = new Button[10];
// 設置每個語音按鈕
for (int i = 0; i < 10; i++)
{
// 根據按鈕索引讀取其圖片資料
var buttonImages = numberWordCountButtonImages[$"button{i}"];
// 創建並初始化語音按鈕,設定其背景圖片
CreateNumberWordCountButton(i, buttonImages.normal, buttonImages.mouseDown, buttonImages.mouseOver);
}
}
/// <summary>
/// 創建一個語音按鈕,並為其設置圖片、座標、事件等屬性。
/// </summary>
/// <param name="index">按鈕的索引,用來獲取對應的語音符號、座標和圖片資料。</param>
/// <param name="normalImagePath">正常狀態下的圖片路徑。</param>
/// <param name="mouseDownImagePath">點擊狀態下的圖片路徑。</param>
/// <param name="mouseOverImagePath">滑鼠移過狀態下的圖片路徑。</param>
private void CreateNumberWordCountButton(int index, string normalImagePath, string mouseDownImagePath, string mouseOverImagePath)
{
try
{
// 創建語音按鈕並設置其屬性
numberWordCountButtonsForSingers[index] = new Button
{
Name = $"numberWordCountButton_{numberWordCountSymbols[index]}", // 按鈕名稱設為語音符號名稱
BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath)), // 設定背景圖片
BackgroundImageLayout = ImageLayout.Stretch, // 設定圖片拉伸樣式
FlatStyle = FlatStyle.Flat, // 設定為平面風格
FlatAppearance = { BorderSize = 0 } // 設定無邊框
};
// 調整按鈕大小並設置位置
ResizeAndPositionButton(numberWordCountButtonsForSingers[index], numberWordCountButtonCoords[index].X, numberWordCountButtonCoords[index].Y,
numberWordCountButtonCoords[index].Width, numberWordCountButtonCoords[index].Height);
// 從檔案中讀取正常、點擊和滑鼠懸停狀態的圖片
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));
// 設置滑鼠事件:點擊、進入、離開等,改變按鈕的背景圖片
numberWordCountButtonsForSingers[index].MouseDown += (s, e) => numberWordCountButtonsForSingers[index].BackgroundImage = mouseDownImage;
numberWordCountButtonsForSingers[index].MouseUp += (s, e) => numberWordCountButtonsForSingers[index].BackgroundImage = normalImage;
numberWordCountButtonsForSingers[index].MouseEnter += (s, e) => numberWordCountButtonsForSingers[index].BackgroundImage = mouseOverImage;
numberWordCountButtonsForSingers[index].MouseLeave += (s, e) => numberWordCountButtonsForSingers[index].BackgroundImage = normalImage;
// 設置點擊事件處理方法
numberWordCountButtonsForSingers[index].Click += NumberWordCountButton_Click;
// 設置按鈕的 Tag 屬性為對應的語音符號
numberWordCountButtonsForSingers[index].Tag = numberWordCountSymbols[index];
// 將按鈕添加到表單的控制項集合中
this.Controls.Add(numberWordCountButtonsForSingers[index]);
}
catch (Exception ex)
{
// 捕捉錯誤並輸出錯誤訊息
Console.WriteLine($"Error creating button at index {index}: {ex.Message}");
}
}
/// <summary>
/// 初始化所有與注音歌手相關的按鈕,包括語音符號按鈕、特殊按鈕及輸入框。
/// </summary>
private void InitializeButtonsForWordCountSingers()
{
// 從配置檔案加載注音符號並初始化按鈕
LoadNumberWordCountSymbolsFromConfig();
// 初始化所有語音按鈕
InitializeNumberWordCountButtons();
// 初始化注音歌手的特殊按鈕(例如音量、搜尋等)
InitializeSpecialButtonsForWordCountSingers();
// 初始化注音歌手的輸入框
InitializeInputBoxWordCountSingers();
}
/// <summary>
/// 移除圖像周圍的白色邊框,將邊框的像素透明化。
/// </summary>
/// <param name="imagePath">待處理的圖像文件路徑。</param>
/// <returns>處理後的圖像,其中白色邊框已被去除並替換為透明。</returns>
private Image RemoveWhiteBorderForWordCountSingers(string imagePath)
{
// 創建一個 Bitmap 物件來加載圖像
Bitmap bmp = new Bitmap(imagePath);
// 定義圖像的矩形區域,這是我們將要操作的區域
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
// 鎖定圖像的位圖數據,以便進行直接修改
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0; // 獲取位圖數據的起始位置
int bytes = Math.Abs(bmpData.Stride) * bmp.Height; // 計算圖像的總字節數
byte[] rgbValues = new byte[bytes]; // 用來存儲圖像的像素數據
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); // 從圖像數據中複製像素數據到 rgbValues 陣列
// 遍歷每個像素點,檢查是否為白色邊框
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x++)
{
int position = (y * bmpData.Stride) + (x * 4); // 計算當前像素的位址
byte b = rgbValues[position]; // 藍色分量
byte g = rgbValues[position + 1]; // 綠色分量
byte r = rgbValues[position + 2]; // 紅色分量
byte a = rgbValues[position + 3]; // alpha 分量(透明度)
// 如果當前像素在圖像邊緣且為白色 (255, 255, 255),則將其設為透明
if ((x < 5 || x > bmp.Width - 5 || y < 5 || y > bmp.Height - 5) && r == 255 && g == 255 && b == 255)
{
// 將白色像素的 RGB 設置為 255, 255, 255 且 alpha 設為 0 (透明)
rgbValues[position] = 255;
rgbValues[position + 1] = 255;
rgbValues[position + 2] = 255;
rgbValues[position + 3] = 0; // 透明
}
}
}
// 將修改後的像素數據重新複製回位圖數據
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// 解鎖圖像數據
bmp.UnlockBits(bmpData);
// 返回處理後的圖像
return bmp;
}
/// <summary>
/// 初始化與注音歌手相關的特殊按鈕,包括修改、清除和關閉按鈕。
/// </summary>
private void InitializeSpecialButtonsForWordCountSingers()
{
// 初始化修改按鈕
InitializeModifyButtonWordCountSingers();
// 初始化清除按鈕
InitializeClearButtonWordCountSingers();
// 初始化關閉按鈕
InitializeCloseButtonWordCountSingers();
}
/// <summary>
/// 初始化「修改」按鈕,設定按鈕的坐標、圖片和點擊事件。
/// </summary>
private void InitializeModifyButtonWordCountSingers()
{
// 加載配置數據
var data = LoadConfigDataforWordCountSingers();
// 讀取按鈕坐標
modifyButtonWordCountCoords = LoadSpecialButtonCoordinatesForWordCountSingers(data, "SpecialButtonCoordinates", "modifyButtonWordCountSingers");
// 加載按鈕圖片(正常、鼠標懸停、鼠標按下)
var buttonImages = LoadButtonImagesForWordCountSingers(data, "ModifyButtonImagesWordCount");
// 創建「修改」按鈕,並設置坐標、圖片及點擊事件
modifyButtonWordCountSingers = CreateSpecialButtonForWordCountSingers(
"btnModifyWordCountSingers", // 按鈕名稱
modifyButtonWordCountCoords, // 按鈕坐標
buttonImages.normal, // 正常狀態圖片
buttonImages.mouseOver, // 鼠標懸停圖片
buttonImages.mouseDown, // 鼠標按下圖片
ModifyButtonWordCountSingers_Click // 按鈕點擊事件
);
}
/// <summary>
/// 處理「修改」按鈕的點擊事件,該事件會刪除輸入框中的最後一個字符。
/// </summary>
/// <param name="sender">觸發事件的對象</param>
/// <param name="e">事件參數</param>
private void ModifyButtonWordCountSingers_Click(object sender, EventArgs e)
{
// 如果輸入框不為空,且包含輸入內容,則刪除最後一個字符
if (this.Controls.Contains(inputBoxWordCountSingers) && inputBoxWordCountSingers.Text.Length > 0)
{
inputBoxWordCountSingers.Text = inputBoxWordCountSingers.Text.Substring(0, inputBoxWordCountSingers.Text.Length - 1);
}
}
/// <summary>
/// 初始化「清除」按鈕,設定按鈕的坐標、圖片和點擊事件。
/// </summary>
private void InitializeClearButtonWordCountSingers()
{
// 加載配置數據
var data = LoadConfigDataforWordCountSingers();
// 讀取按鈕坐標
clearButtonWordCountCoords = LoadSpecialButtonCoordinatesForWordCountSingers(data, "SpecialButtonCoordinates", "clearButtonWordCountSingers");
// 加載按鈕圖片(正常、鼠標懸停、鼠標按下)
var buttonImages = LoadButtonImagesForWordCountSingers(data, "ClearButtonImagesWordCount");
// 創建「清除」按鈕,並設置坐標、圖片及點擊事件
clearButtonWordCountSingers = CreateSpecialButtonForWordCountSingers(
"btnClearWordCountSingers", // 按鈕名稱
clearButtonWordCountCoords, // 按鈕坐標
buttonImages.normal, // 正常狀態圖片
buttonImages.mouseOver, // 鼠標懸停圖片
buttonImages.mouseDown, // 鼠標按下圖片
ClearButtonWordCountSingers_Click // 按鈕點擊事件
);
}
/// <summary>
/// 處理「清除」按鈕的點擊事件,該事件會清空輸入框中的所有文本。
/// </summary>
/// <param name="sender">觸發事件的對象</param>
/// <param name="e">事件參數</param>
private void ClearButtonWordCountSingers_Click(object sender, EventArgs e)
{
// 如果輸入框不為空,則清空該框的文本內容
if (this.Controls.Contains(inputBoxWordCountSingers) && inputBoxWordCountSingers.Text.Length > 0)
{
inputBoxWordCountSingers.Text = "";
}
}
/// <summary>
/// 初始化「關閉」按鈕,設定按鈕的坐標、圖片和點擊事件。
/// </summary>
private void InitializeCloseButtonWordCountSingers()
{
// 加載配置數據
var data = LoadConfigDataforWordCountSingers();
// 讀取按鈕坐標
closeButtonWordCountCoords = LoadSpecialButtonCoordinatesForWordCountSingers(data, "SpecialButtonCoordinates", "closeButtonWordCountSingers");
// 加載按鈕圖片(正常、鼠標懸停、鼠標按下)
var buttonImages = LoadButtonImagesForWordCountSingers(data, "CloseButtonImagesWordCount");
// 創建「關閉」按鈕,並設置坐標、圖片及點擊事件
closeButtonWordCountSingers = CreateSpecialButtonForWordCountSingers(
"btnCloseWordCountSingers", // 按鈕名稱
closeButtonWordCountCoords, // 按鈕坐標
buttonImages.normal, // 正常狀態圖片
buttonImages.mouseOver, // 鼠標懸停圖片
buttonImages.mouseDown, // 鼠標按下圖片
CloseButtonWordCountSingers_Click // 按鈕點擊事件
);
}
/// <summary>
/// 「關閉」按鈕的點擊事件處理方法。
/// 隱藏 WordCount 歌手圖片框以及與其相關的按鈕。
/// </summary>
/// <param name="sender">觸發事件的對象,這裡是關閉按鈕。</param>
/// <param name="e">事件參數。</param>
private void CloseButtonWordCountSingers_Click(object sender, EventArgs e)
{
// 隱藏 WordCount 歌手圖片框
pictureBoxWordCountSingers.Visible = false;
wordCountSearchButton.BackgroundImage = wordCountSearchNormalBackground;
// 隱藏與 WordCount 歌手相關的所有按鈕
SetWordCountSingersAndButtonsVisibility(false);
}
/// <summary>
/// 創建一個特殊的按鈕,並設定其顯示屬性、事件處理和位置。
/// </summary>
/// <param name="name">按鈕的名稱。</param>
/// <param name="coords">按鈕的坐標和大小,包含 X, Y, 寬度和高度。</param>
/// <param name="normalImagePath">按鈕正常狀態下的背景圖片路徑。</param>
/// <param name="mouseOverImagePath">鼠標懸停時按鈕的背景圖片路徑。</param>
/// <param name="mouseDownImagePath">鼠標點擊時按鈕的背景圖片路徑。</param>
/// <param name="clickEventHandler">按鈕的點擊事件處理程序。</param>
/// <returns>創建並返回的按鈕對象。</returns>
private Button CreateSpecialButtonForWordCountSingers(string name, (int X, int Y, int Width, int Height) coords, string normalImagePath, string mouseOverImagePath, string mouseDownImagePath, EventHandler clickEventHandler)
{
// 創建按鈕並設定基本屬性
var button = new Button
{
Name = name,
FlatStyle = FlatStyle.Flat,
FlatAppearance = { BorderSize = 0, MouseDownBackColor = Color.Transparent, MouseOverBackColor = Color.Transparent },
BackgroundImageLayout = ImageLayout.Stretch,
BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath))
};
// 設定按鈕的大小和位置
ResizeAndPositionButton(button, coords.X, coords.Y, coords.Width, coords.Height);
// 設定鼠標事件:進入、離開、按下、放開
button.MouseEnter += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, mouseOverImagePath));
button.MouseLeave += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath));
button.MouseDown += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, mouseDownImagePath));
button.MouseUp += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath));
// 註冊點擊事件處理
button.Click += clickEventHandler;
// 將按鈕添加到控件集合中
this.Controls.Add(button);
return button;
}
/// <summary>
/// 初始化 WordCount 歌手的輸入框,並設定其屬性與事件處理程序。
/// </summary>
private void InitializeInputBoxWordCountSingers()
{
try
{
// 加載輸入框配置
LoadInputBoxConfigForWordCountSingers();
// 創建一個 RichTextBox 控件來作為輸入框
inputBoxWordCountSingers = new RichTextBox
{
Name = "inputBoxWordCountSingers",
ForeColor = inputBoxWordCountForeColor, // 設定文字顏色
Font = new Font(inputBoxWordCountFontName, inputBoxWordCountFontSize, inputBoxWordCountFontStyle), // 設定字體樣式
ScrollBars = RichTextBoxScrollBars.None // 不顯示滾動條
};
// 調整和定位輸入框的位置及大小
ResizeAndPositionControl(inputBoxWordCountSingers, inputBoxWordCountCoords.X, inputBoxWordCountCoords.Y, inputBoxWordCountCoords.Width, inputBoxWordCountCoords.Height);
// 設定文本變更事件,當輸入框內容改變時觸發
inputBoxWordCountSingers.TextChanged += (sender, e) =>
{
string searchText = inputBoxWordCountSingers.Text; // 取得輸入內容
if (int.TryParse(searchText, out int length))
{
var searchResults = allArtists.Where(artist => artist.Name.Length == length).ToList();
// 設定當前頁數為 0並加載搜索結果
currentPage = 0;
currentArtistList = searchResults;
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSingers(currentArtistList);
}
};
// 將輸入框加入到窗體的控件集合中
this.Controls.Add(inputBoxWordCountSingers);
}
catch (Exception ex)
{
// 如果初始化過程中出現錯誤,則在控制台輸出錯誤信息
Console.WriteLine("Error initializing inputBoxWordCountSingers: " + ex.Message);
}
}
/// <summary>
/// 從配置文件 `config.ini` 中加載輸入框的設置,包括位置、大小、字體等屬性。
/// </summary>
private void LoadInputBoxConfigForWordCountSingers()
{
try
{
// 創建 INI 解析器
var parser = new FileIniDataParser();
string iniFilePath = "config.ini"; // 配置文件的路徑
IniData data;
// 打開並讀取配置文件
using (var reader = new StreamReader(iniFilePath, Encoding.UTF8))
{
data = parser.ReadData(reader);
}
// 從配置中加載輸入框的坐標和大小
inputBoxWordCountCoords = (
int.Parse(data["InputBoxWordCountSingers"]["X"]), // 輸入框的 X 坐標
int.Parse(data["InputBoxWordCountSingers"]["Y"]), // 輸入框的 Y 坐標
int.Parse(data["InputBoxWordCountSingers"]["Width"]), // 輸入框的寬度
int.Parse(data["InputBoxWordCountSingers"]["Height"]) // 輸入框的高度
);
// 從配置中加載字體屬性
inputBoxWordCountFontName = data["InputBoxWordCountSingers"]["FontName"]; // 字體名稱
inputBoxWordCountFontSize = float.Parse(data["InputBoxWordCountSingers"]["FontSize"]); // 字體大小
inputBoxWordCountFontStyle = (FontStyle)Enum.Parse(typeof(FontStyle), data["InputBoxWordCountSingers"]["FontStyle"]); // 字體樣式
inputBoxWordCountForeColor = Color.FromName(data["InputBoxWordCountSingers"]["ForeColor"]); // 字體顏色
}
catch (Exception ex)
{
// 若發生錯誤,顯示錯誤信息
Console.WriteLine("Error loading inputBox configuration: " + ex.Message);
}
}
/// <summary>
/// 存儲 `pictureBoxWordCountSingers` 控制項的坐標和大小設置。
/// </summary>
/// <remarks>
/// 這個元組包含了 `X`、`Y` 坐標以及 `Width`、`Height` 大小,用於配置 `pictureBoxWordCountSingers` 的位置和大小。
/// </remarks>
private (int X, int Y, int Width, int Height) pictureBoxWordCountSingerCoords;
/// <summary>
/// 從配置檔案中讀取 `PictureBoxWordCountSingers` 控制項的坐標和大小設置。
/// </summary>
private void LoadPictureBoxWordCountSingerCoordsFromConfig()
{
var parser = new FileIniDataParser();
IniData data = parser.ReadFile("config.ini");
var coords = data["PictureBoxWordCountSingers"];
pictureBoxWordCountSingerCoords = (
int.Parse(coords["X"]),
int.Parse(coords["Y"]),
int.Parse(coords["Width"]),
int.Parse(coords["Height"])
);
}
/// <summary>
/// 顯示圖片並根據配置文件設置顯示區域的大小和位置。
/// </summary>
/// <param name="imagePath">圖片的路徑。</param>
private void ShowImageOnPictureBoxWordCountSingers(string imagePath)
{
// 讀取配置文件中的顯示區域設置
LoadPictureBoxWordCountSingerCoordsFromConfig();
// 加載原始圖片
Bitmap originalImage = new Bitmap(imagePath);
// 創建顯示區域,根據配置文件中的坐標和大小設置
Rectangle displayArea = new Rectangle(pictureBoxWordCountSingerCoords.X, pictureBoxWordCountSingerCoords.Y, pictureBoxWordCountSingerCoords.Width, pictureBoxWordCountSingerCoords.Height);
// 設置圖片到 PictureBox
pictureBoxWordCountSingers.Image = originalImage;
// 調整 PictureBox 大小和位置
ResizeAndPositionPictureBox(pictureBoxWordCountSingers, displayArea.X, displayArea.Y, displayArea.Width, displayArea.Height);
// 顯示圖片
pictureBoxWordCountSingers.Visible = true;
}
/// <summary>
/// 設置注音歌手相關控制項(包括圖片框、按鈕和輸入框)的顯示或隱藏狀態。
/// </summary>
/// <param name="isVisible">指定控件是否可見。True 為顯示False 為隱藏。</param>
private void SetWordCountSingersAndButtonsVisibility(bool isVisible)
{
// 定義一個動作來處理控制項的顯示或隱藏
System.Action action = () =>
{
try
{
// 暫停控制項佈局的重新排版,提高效率
SuspendLayout();
// 檢查並設置圖片框的可見性
if (pictureBoxWordCountSingers == null)
{
Console.WriteLine("pictureBoxWordCountSingers is null");
}
else
{
pictureBoxWordCountSingers.Visible = isVisible;
if (isVisible) pictureBoxWordCountSingers.BringToFront(); // 如果顯示,將其置於最前
}
// 檢查並設置拼音按鈕的可見性
if (numberWordCountButtonsForSingers == null)
{
Console.WriteLine("numberWordCountButtonsForSingers is null");
}
else
{
foreach (var button in numberWordCountButtonsForSingers)
{
if (button == null)
{
Console.WriteLine("One of the numberWordCountButtonsForSingers is null");
}
else
{
button.Visible = isVisible;
if (isVisible) button.BringToFront(); // 如果顯示,將其置於最前
}
}
}
// 檢查並設置修改按鈕的可見性
if (modifyButtonWordCountSingers == null)
{
Console.WriteLine("modifyButtonWordCountSingers is null");
}
else
{
modifyButtonWordCountSingers.Visible = isVisible;
if (isVisible) modifyButtonWordCountSingers.BringToFront(); // 如果顯示,將其置於最前
}
// 檢查並設置清除按鈕的可見性
if (clearButtonWordCountSingers == null)
{
Console.WriteLine("clearButtonWordCountSingers is null");
}
else
{
clearButtonWordCountSingers.Visible = isVisible;
if (isVisible) clearButtonWordCountSingers.BringToFront(); // 如果顯示,將其置於最前
}
// 檢查並設置關閉按鈕的可見性
if (closeButtonWordCountSingers == null)
{
Console.WriteLine("closeButtonWordCountSingers is null");
}
else
{
closeButtonWordCountSingers.Visible = isVisible;
if (isVisible) closeButtonWordCountSingers.BringToFront(); // 如果顯示,將其置於最前
}
// 檢查並設置輸入框的可見性
if (inputBoxWordCountSingers == null)
{
Console.WriteLine("inputBoxWordCountSingers is null");
}
else
{
inputBoxWordCountSingers.Visible = isVisible;
if (isVisible) inputBoxWordCountSingers.BringToFront(); // 如果顯示,將其置於最前
}
// 恢復控制項的佈局重新排版
ResumeLayout();
PerformLayout();
// 刷新所有控制項的顯示
pictureBoxWordCountSingers?.Refresh();
if (numberWordCountButtonsForSingers != null)
{
foreach (var button in numberWordCountButtonsForSingers)
{
button?.Refresh(); // 刷新每個按鈕
}
}
modifyButtonWordCountSingers?.Refresh();
clearButtonWordCountSingers?.Refresh();
closeButtonWordCountSingers?.Refresh();
inputBoxWordCountSingers?.Refresh();
}
catch (Exception ex)
{
Console.WriteLine("Error in SetWordCountSingersAndButtonsVisibility: " + ex.Message);
}
};
// 檢查是否需要在主執行緒外執行
if (this.InvokeRequired)
{
this.Invoke(action); // 如果需要,透過主執行緒執行
}
else
{
action(); // 否則直接執行
}
}
private void NumberWordCountButton_Click(object sender, EventArgs e)
{
var button = sender as Button;
if (button != null && button.Tag != null)
{
if (inputBoxWordCountSingers.Visible)
{
inputBoxWordCountSingers.Text += button.Tag.ToString();
}
}
}
}
}

View File

@ -0,0 +1,113 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private Button zhuyinSearchButton;
private Bitmap zhuyinSearchNormalBackground;
private Bitmap zhuyinSearchActiveBackground;
private Button englishSearchButton;
private Bitmap englishSearchNormalBackground;
private Bitmap englishSearchActiveBackground;
private Button pinyinSearchButton;
private Bitmap pinyinSearchNormalBackground;
private Bitmap pinyinSearchActiveBackground;
private Button wordCountSearchButton;
private Bitmap wordCountSearchNormalBackground;
private Bitmap wordCountSearchActiveBackground;
private Button handWritingSearchButton;
private Bitmap handWritingSearchNormalBackground;
private Bitmap handWritingSearchActiveBackground;
private void SingerSearchButton_Click(object sender, EventArgs e)
{
newSongAlertButton.BackgroundImage = newSongAlertNormalBackground;
hotPlayButton.BackgroundImage = hotPlayNormalBackground;
singerSearchButton.BackgroundImage = singerSearchActiveBackground;
songSearchButton.BackgroundImage = songSearchNormalBackground;
languageSearchButton.BackgroundImage = languageSearchNormalBackground;
groupSearchButton.BackgroundImage = groupSearchNormalBackground;
categorySearchButton.BackgroundImage = categorySearchNormalBackground;
orderedSongsButton.BackgroundImage = orderedSongsNormalBackground;
myFavoritesButton.BackgroundImage = myFavoritesNormalBackground;
promotionsButton.BackgroundImage = promotionsNormalBackground;
deliciousFoodButton.BackgroundImage = deliciousFoodNormalBackground;
isOnOrderedSongsPage = false;
SetHotSongButtonsVisibility(false);
SetNewSongButtonsVisibility(false);
SetSongSearchButtonsVisibility(false);
SetPictureBoxLanguageButtonsVisibility(false);
SetGroupButtonsVisibility(false);
SetPictureBoxCategoryAndButtonsVisibility(false);
SetZhuYinSingersAndButtonsVisibility(false);
SetZhuYinSongsAndButtonsVisibility(false);
SetEnglishSingersAndButtonsVisibility(false);
SetEnglishSongsAndButtonsVisibility(false);
SetPinYinSingersAndButtonsVisibility(false);
SetPinYinSongsAndButtonsVisibility(false);
SetPictureBoxToggleLightAndButtonsVisibility(false);
SetPictureBoxSceneSoundEffectsAndButtonsVisibility(false);
SetSingerSearchButtonsVisibility(true);
if (pictureBoxQRCode != null)
{
pictureBoxQRCode.Visible = false;
closeQRCodeButton.Visible = false;
}
}
private void SetSingerSearchButtonsVisibility(bool isVisible)
{
Button[] singerSearchButtons = { zhuyinSearchButton, englishSearchButton, pinyinSearchButton, wordCountSearchButton, handWritingSearchButton };
foreach (var button in singerSearchButtons)
{
button.Visible = isVisible;
if (isVisible)
{
button.BringToFront();
}
}
}
private void InitializeButtonsForSingerSearch()
{
InitializeSearchButton(ref zhuyinSearchButton, "zhuyinSearchButton", 1214, 230, 209, 59, ref zhuyinSearchNormalBackground, ref zhuyinSearchActiveBackground, normalStateImageArtistQuery, mouseDownImageArtistQuery, ZhuyinSearchSingersButton_Click);
InitializeSearchButton(ref englishSearchButton, "englishSearchButton", 1214, 293, 209, 58, ref englishSearchNormalBackground, ref englishSearchActiveBackground, normalStateImageArtistQuery, mouseDownImageArtistQuery, EnglishSearchSingersButton_Click);
InitializeSearchButton(ref pinyinSearchButton, "pinyinSearchButton", 1214, 356, 209, 58, ref pinyinSearchNormalBackground, ref pinyinSearchActiveBackground, normalStateImageArtistQuery, mouseDownImageArtistQuery, PinyinSingerSearchButton_Click);
InitializeSearchButton(ref wordCountSearchButton, "workCountSearchButton", 1214, 418, 209, 59, ref wordCountSearchNormalBackground, ref wordCountSearchActiveBackground, normalStateImageArtistQuery, mouseDownImageArtistQuery, WordCountSearchSingersButton_Click);
InitializeSearchButton(ref handWritingSearchButton, "handWritingSearchButton", 1214, 481, 209, 59, ref handWritingSearchNormalBackground, ref handWritingSearchActiveBackground, normalStateImageArtistQuery, mouseDownImageArtistQuery, HandWritingSearchButtonForSingers_Click);
}
private void InitializeSearchButton(ref Button button, string name, int x, int y, int width, int height, ref Bitmap normalBackground, ref Bitmap activeBackground, Bitmap normalImage, Bitmap activeImage, EventHandler clickEventHandler)
{
button = new Button { Text = "", Visible = true, Name = name };
ResizeAndPositionButton(button, x, y, width, height);
Rectangle buttonCropArea = new Rectangle(x, y, width, height);
normalBackground = normalImage.Clone(buttonCropArea, normalImage.PixelFormat);
activeBackground = activeImage.Clone(buttonCropArea, activeImage.PixelFormat);
button.BackgroundImage = normalBackground;
button.BackgroundImageLayout = ImageLayout.Stretch;
button.FlatStyle = FlatStyle.Flat;
button.FlatAppearance.BorderSize = 0;
button.Click += clickEventHandler;
this.Controls.Add(button);
}
}
}

View File

@ -0,0 +1,562 @@
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;
/// <summary>
/// 注音歌曲搜尋按鈕點擊事件
/// </summary>
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;
}
/// <summary>
/// 初始化注音按鈕 (Phonetic Buttons) 並載入其對應的圖片與座標
/// <para>1. 讀取 config.ini 設定檔,獲取按鈕的相關數據 (符號、座標、圖片)</para>
/// <para>2. 解析注音符號並儲存至 phoneticSymbols 陣列</para>
/// <para>3. 解析按鈕的座標資訊,存入 phoneticButtonCoords</para>
/// <para>4. 解析按鈕的圖片 (正常狀態、按下狀態、懸停狀態),存入 phoneticButtonImages。</para>
/// <para>5. 依序建立 35 個注音按鈕,並套用對應的圖片與事件處理函數。</para>
/// </summary>
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 // 滑鼠懸停時的圖片
);
}
}
/// <summary>
/// 建立單個注音按鈕,並設定其圖片、大小、位置,以及滑鼠事件。
/// </summary>
/// <param name="index">按鈕索引 (對應於 phoneticSymbols)</param>
/// <param name="normalImagePath">按鈕的普通狀態圖片路徑</param>
/// <param name="mouseDownImagePath">按鈕被按下時的圖片路徑</param>
/// <param name="mouseOverImagePath">滑鼠懸停時的圖片路徑</param>
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}");
}
}
/// <summary>
/// 初始化注音輸入界面的所有按鈕和輸入框。
/// </summary>
private void InitializeButtonsForZhuYinSongs()
{
// 1. 從設定檔 (config.ini) 載入注音符號
LoadPhoneticSymbolsFromConfig();
// 2. 初始化 35 個注音按鈕
InitializePhoneticButtonsForSongs();
// 3. 初始化特殊按鈕 (例如:刪除、確定、返回按鈕)
InitializeSpecialButtonsForZhuYinSongs();
// 4. 初始化輸入框 (用於顯示使用者輸入的注音符號)
InitializeInputBoxZhuYinSongs();
}
/// <summary>
/// 初始化注音輸入界面的特殊按鈕,包括「修改」、「清除」和「關閉」。
/// </summary>
private void InitializeSpecialButtonsForZhuYinSongs()
{
// 1. 初始化「修改」按鈕 (刪除上一個輸入的注音符號)
InitializeModifyButtonZhuYinSongs();
// 2. 初始化「清除」按鈕 (清空所有輸入內容)
InitializeClearButtonZhuYinSongs();
// 3. 初始化「關閉」按鈕 (關閉注音輸入介面)
InitializeCloseButtonZhuYinSongs();
}
/// <summary>
/// 初始化「修改」按鈕,讓使用者可以刪除上一個輸入的注音符號。
/// </summary>
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 // 綁定按鈕點擊事件
);
}
/// <summary>
/// 「修改」按鈕的點擊事件,刪除輸入框內的最後一個注音符號。
/// </summary>
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);
}
}
/// <summary>
/// 初始化「清除」按鈕,並從設定檔載入其位置與圖片資源。
/// </summary>
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 // 點擊事件處理函式
);
}
/// <summary>
/// 當使用者點擊「清除」按鈕時,將輸入框 (inputBoxZhuYinSongs) 的內容清空。
/// </summary>
/// <param name="sender">觸發事件的按鈕。</param>
/// <param name="e">事件參數。</param>
private void ClearButtonZhuYinSongs_Click(object sender, EventArgs e)
{
// 1. 確保視窗內包含「注音輸入框」(inputBoxZhuYinSongs),且輸入框內有文字
if (this.Controls.Contains(inputBoxZhuYinSongs) && inputBoxZhuYinSongs.Text.Length > 0)
{
// 2. 清空輸入框內容
inputBoxZhuYinSongs.Text = "";
}
}
/// <summary>
/// 初始化注音輸入的關閉按鈕,從設定檔讀取按鈕座標與圖片,並設置點擊事件
/// </summary>
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 // 綁定點擊事件
);
}
/// <summary>
/// 關閉注音輸入介面,隱藏相關 UI 元件
/// </summary>
/// <param name="sender">觸發事件的按鈕 (關閉按鈕)</param>
/// <param name="e">事件參數</param>
private void CloseButtonZhuYinSongs_Click(object sender, EventArgs e)
{
// 隱藏注音輸入的圖片
pictureBoxZhuYinSongs.Visible = false;
zhuyinSearchSongButton.BackgroundImage = zhuyinSearchSongNormalBackground;
// 隱藏注音輸入的所有按鈕與介面元素
SetZhuYinSongsAndButtonsVisibility(false);
}
/// <summary>
/// 初始化注音輸入框 (RichTextBox),設定外觀、事件處理及位置大小
/// </summary>
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);
}
}
/// <summary>
/// 存儲 PictureBoxZhuYinSongs 的座標與尺寸信息。
/// </summary>
/// <remarks>
/// 此元組包含以下四個值:
/// XX 座標
/// , YY 座標
/// , Width寬度
/// , Height高度
/// </remarks>
private (int X, int Y, int Width, int Height) pictureBoxZhuYinSongCoords;
/// <summary>
/// 從設定檔 (config.ini) 讀取 PictureBoxZhuYinSongs 的座標與尺寸
/// </summary>
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"]) // 讀取高度
);
}
/// <summary>
/// 在 pictureBoxZhuYinSongs 上顯示指定路徑的圖片,並根據設定調整其大小與位置。
/// </summary>
/// <param name="imagePath">要顯示的圖片檔案路徑</param>
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;
}
/// <summary>
/// 設定注音歌曲相關的 PictureBox、按鈕和輸入框的可見性。
/// </summary>
/// <param name="isVisible">若為 true則顯示這些控件否則隱藏。</param>
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();
}
}
}
}

View File

@ -0,0 +1,444 @@
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;
using IniParser;
using IniParser.Model;
using System.Text;
/* 歌曲查詢 英文輸入 */
namespace DualScreenDemo
{
public partial class PrimaryForm
{
/* 主頁面設計 */
private PictureBox pictureBoxEnglishSongs;
private Button[] numberButtonsForSongs;
private Button[] letterButtonsForEnglishSongs;
private Button modifyButtonEnglishSongs;
private Button clearButtonEnglishSongs;
private Button closeButtonEnglishSongs;
private RichTextBox inputBoxEnglishSongs;
private void EnglishSearchSongsButton_Click(object sender, EventArgs e)
{
zhuyinSearchSongButton.BackgroundImage = zhuyinSearchSongNormalBackground;
englishSearchSongButton.BackgroundImage = englishSearchSongActiveBackground;
pinyinSearchSongButton.BackgroundImage = pinyinSearchSongNormalBackground;
wordCountSearchSongButton.BackgroundImage = wordCountSearchSongNormalBackground;
handWritingSearchSongButton.BackgroundImage = handWritingSearchSongNormalBackground;
numberSearchSongButton.BackgroundImage = numberSearchSongNormalBackground;
bool shouldBeVisible = !pictureBoxEnglishSongs.Visible;
var configData = LoadConfigData();
string imagePath = Path.Combine(Application.StartupPath, configData["ImagePaths"]["EnglishSongs"]);
ShowImageOnPictureBoxEnglishSongs(Path.Combine(Application.StartupPath, imagePath));
// 鍵盤UI介面顯示設定
SetWordCountSongsAndButtonsVisibility(false);
SetEnglishSongsAndButtonsVisibility(true);
SetPinYinSongsAndButtonsVisibility(false);
SetHandWritingForSongsAndButtonsVisibility(false);
SetSongIDSearchAndButtonsVisibility(false);
SetZhuYinSongsAndButtonsVisibility(false);
ResetinputBox();
pictureBoxEnglishSongs.Visible = true;
}
private void InitializeNumberButtonsForSongs()
{
var data = LoadConfigData();
numberButtonCoords = LoadButtonCoordinates(data, "NumberButtonCoordinates", 10);
var buttonImages = LoadButtonImages(data, "NumberButtonImages", 10);
/* 按鈕圖片路徑設置 */
numberButtonsForSongs = new Button[10];
for (int i = 0; i < 10; i++)
{
string normalImagePath = buttonImages[$"button{i}"].normal;
string mouseDownImagePath = buttonImages[$"button{i}"].mouseDown;
string mouseOverImagePath = buttonImages[$"button{i}"].mouseOver;
if (normalImagePath == null || mouseDownImagePath == null || mouseOverImagePath == null)
{
Console.WriteLine($"Error: One or more image paths for button{i} are null.");
continue;
}
numberButtonsForSongs[i] = CreateButton(
$"numberButton_{i}",
numberButtonCoords[i],
normalImagePath,
mouseDownImagePath,
mouseOverImagePath,
NumberButtonForSongs_Click
);
numberButtonsForSongs[i].Tag = (i + 1) % 10;
this.Controls.Add(numberButtonsForSongs[i]);
}
}
/* 按鈕按下事件 */
private void NumberButtonForSongs_Click(object sender, EventArgs e)
{
var button = sender as Button;
if (button != null && button.Tag != null)
{
if (inputBoxEnglishSongs.Visible)
{
inputBoxEnglishSongs.Text += button.Tag.ToString();
}
}
}
/* 鍵盤對應按鈕位置 */
private void InitializeLetterButtonsForEnglishSongs()
{
var data = LoadConfigData();
var buttonImages = LoadButtonImages(data, "EnglishLetterButtonImages", 26);
string qwertyLayout = "QWERTYUIOPASDFGHJKLZXCVBNM";
letterButtonsForEnglishSongs = new Button[26];
for (int i = 0; i < 26; i++)
{
var coords = data["EnglishLetterButtonCoordinates"][$"button{i}"].Split(',');
letterButtonsForEnglishSongs[i] = CreateButton(
$"letterButton_{qwertyLayout[i]}",
(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,
LetterButtonEnglishSongs_Click
);
letterButtonsForEnglishSongs[i].Tag = qwertyLayout[i];
this.Controls.Add(letterButtonsForEnglishSongs[i]);
}
}
private void LetterButtonEnglishSongs_Click(object sender, EventArgs e)
{
var button = sender as Button;
if (button != null && button.Tag != null)
{
if (inputBoxEnglishSongs.Visible)
{
inputBoxEnglishSongs.Text += button.Tag.ToString();
}
}
}
private void InitializeButtonsForEnglishSongs()
{
InitializeNumberButtonsForSongs();
InitializeLetterButtonsForEnglishSongs();
InitializeModifyButtonEnglishSongs();
InitializeClearButtonEnglishSongs();
InitializeCloseButtonEnglishSongs();
InitializeInputBoxEnglishSongs();
}
private void InitializeModifyButtonEnglishSongs()
{
var data = LoadConfigData();
modifyButtonEnglishCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "modifyButtonEnglishSongs");
var buttonImages = LoadButtonImages(data, "ModifyButtonImagesEnglish");
modifyButtonEnglishSongs = CreateSpecialButton(
"btnModifyEnglishSongs",
modifyButtonEnglishCoords,
buttonImages.normal,
buttonImages.mouseOver,
buttonImages.mouseDown,
ModifyButtonEnglishSongs_Click
);
this.Controls.Add(modifyButtonEnglishSongs);
}
private void ModifyButtonEnglishSongs_Click(object sender, EventArgs e)
{
if (this.Controls.Contains(inputBoxEnglishSongs) && inputBoxEnglishSongs.Text.Length > 0)
{
inputBoxEnglishSongs.Text = inputBoxEnglishSongs.Text.Substring(0, inputBoxEnglishSongs.Text.Length - 1);
}
}
private void InitializeClearButtonEnglishSongs()
{
var data = LoadConfigData();
clearButtonEnglishCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "clearButtonEnglishSongs");
var buttonImages = LoadButtonImages(data, "ClearButtonImagesEnglish");
clearButtonEnglishSongs = CreateSpecialButton(
"btnClearEnglishSongs",
clearButtonEnglishCoords,
buttonImages.normal,
buttonImages.mouseOver,
buttonImages.mouseDown,
ClearButtonEnglishSongs_Click
);
this.Controls.Add(clearButtonEnglishSongs);
}
private void ClearButtonEnglishSongs_Click(object sender, EventArgs e)
{
if (this.Controls.Contains(inputBoxEnglishSongs) && inputBoxEnglishSongs.Text.Length > 0)
{
inputBoxEnglishSongs.Text = "";
}
}
private void InitializeCloseButtonEnglishSongs()
{
var data = LoadConfigData();
closeButtonEnglishCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "closeButtonEnglishSongs");
var buttonImages = LoadButtonImages(data, "CloseButtonImagesEnglish");
closeButtonEnglishSongs = CreateSpecialButton(
"btnCloseEnglishSongs",
closeButtonEnglishCoords,
buttonImages.normal,
buttonImages.mouseOver,
buttonImages.mouseDown,
CloseButtonEnglishSongs_Click
);
this.Controls.Add(closeButtonEnglishSongs);
}
private void CloseButtonEnglishSongs_Click(object sender, EventArgs e)
{
pictureBoxEnglishSongs.Visible = false;
englishSearchSongButton.BackgroundImage = englishSearchSongNormalBackground;
SetEnglishSongsAndButtonsVisibility(false);
}
private void InitializeInputBoxEnglishSongs()
{ /* 英文輸入介面設定,參考 config.ini */
try
{
var parser = new FileIniDataParser();
parser.Parser.Configuration.AssigmentSpacer = "";
parser.Parser.Configuration.CommentString = "#";
parser.Parser.Configuration.CaseInsensitive = true;
IniData data;
using (var reader = new StreamReader("config.ini", System.Text.Encoding.UTF8))
{
data = parser.ReadData(reader);
}
int x = int.Parse(data["InputBoxEnglishSongs"]["X"]);
int y = int.Parse(data["InputBoxEnglishSongs"]["Y"]);
int width = int.Parse(data["InputBoxEnglishSongs"]["Width"]);
int height = int.Parse(data["InputBoxEnglishSongs"]["Height"]);
string fontName = data["InputBoxEnglishSongs"]["FontName"];
float fontSize = float.Parse(data["InputBoxEnglishSongs"]["FontSize"]);
FontStyle fontStyle = (FontStyle)Enum.Parse(typeof(FontStyle), data["InputBoxEnglishSongs"]["FontStyle"]);
Color foreColor = Color.FromName(data["InputBoxEnglishSongs"]["ForeColor"]);
inputBoxEnglishSongs = new RichTextBox
{
Visible = false,
Name = "inputBoxEnglishSongs",
ForeColor = foreColor,
Font = new Font(fontName, fontSize / 900 * Screen.PrimaryScreen.Bounds.Height, fontStyle)
};
ResizeAndPositionControl(inputBoxEnglishSongs, x, y, width, height);
/*搜尋結果 傳回點歌介面*/
inputBoxEnglishSongs.TextChanged += (sender, e) =>
{
string searchText = inputBoxEnglishSongs.Text;
var searchResults = allSongs.Where(song => song.Song.StartsWith(searchText)).ToList();
currentPage = 0;
currentSongList = searchResults;
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
};
this.Controls.Add(inputBoxEnglishSongs);
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
/* 圖片位置設置 */
private void ShowImageOnPictureBoxEnglishSongs(string imagePath)
{
try
{
var parser = new FileIniDataParser();
parser.Parser.Configuration.AssigmentSpacer = "";
parser.Parser.Configuration.CommentString = "#";
parser.Parser.Configuration.CaseInsensitive = true;
IniData data;
using (var reader = new StreamReader("config.ini", System.Text.Encoding.UTF8))
{
data = parser.ReadData(reader);
}
int x = int.Parse(data["PictureBoxEnglishSongs"]["X"]);
int y = int.Parse(data["PictureBoxEnglishSongs"]["Y"]);
int width = int.Parse(data["PictureBoxEnglishSongs"]["Width"]);
int height = int.Parse(data["PictureBoxEnglishSongs"]["Height"]);
Bitmap originalImage = new Bitmap(imagePath);
pictureBoxEnglishSongs.Image = originalImage;
ResizeAndPositionPictureBox(pictureBoxEnglishSongs, x, y, width, height);
pictureBoxEnglishSongs.Visible = true;
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
private void SetEnglishSongsAndButtonsVisibility(bool isVisible)
{
System.Action action = () =>
{
SuspendLayout();
if (pictureBoxEnglishSongs == null)
{
Console.WriteLine("pictureBoxEnglishSongs is null");
}
else
{
pictureBoxEnglishSongs.Visible = isVisible;
if (isVisible) pictureBoxEnglishSongs.BringToFront();
pictureBoxEnglishSongs.Refresh();
}
if (numberButtonsForSongs == null)
{
Console.WriteLine("numberButtonsForSongs is null");
}
else
{
foreach (var button in numberButtonsForSongs)
{
if (button == null)
{
Console.WriteLine("A button in numberButtonsForSongs is null");
}
else
{
button.Visible = isVisible;
if (isVisible) button.BringToFront();
button.Refresh();
}
}
}
if (letterButtonsForEnglishSongs == null)
{
Console.WriteLine("letterButtonsForEnglishSongs is null");
}
else
{
foreach (var button in letterButtonsForEnglishSongs)
{
if (button == null)
{
Console.WriteLine("A button in letterButtonsForEnglishSongs is null");
}
else
{
button.Visible = isVisible;
if (isVisible) button.BringToFront();
button.Refresh();
}
}
}
if (modifyButtonEnglishSongs == null)
{
Console.WriteLine("modifyButtonEnglishSongs is null");
}
else
{
modifyButtonEnglishSongs.Visible = isVisible;
if (isVisible) modifyButtonEnglishSongs.BringToFront();
modifyButtonEnglishSongs.Refresh();
}
if (clearButtonEnglishSongs == null)
{
Console.WriteLine("clearButtonEnglishSongs is null");
}
else
{
clearButtonEnglishSongs.Visible = isVisible;
if (isVisible) clearButtonEnglishSongs.BringToFront();
clearButtonEnglishSongs.Refresh();
}
if (closeButtonEnglishSongs == null)
{
Console.WriteLine("closeButtonEnglishSongs is null");
}
else
{
closeButtonEnglishSongs.Visible = isVisible;
if (isVisible) closeButtonEnglishSongs.BringToFront();
closeButtonEnglishSongs.Refresh();
}
if (inputBoxEnglishSongs == null)
{
Console.WriteLine("inputBoxEnglishSongs is null");
}
else
{
inputBoxEnglishSongs.Visible = isVisible;
if (isVisible) inputBoxEnglishSongs.BringToFront();
inputBoxEnglishSongs.Refresh();
}
ResumeLayout();
PerformLayout();
};
if (this.InvokeRequired)
{
this.Invoke(action);
}
else
{
action();
}
}
}
}

View File

@ -0,0 +1,305 @@
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using Microsoft.Ink;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private PictureBox pictureBoxHandWritingSongs;
private Button refillButtonHandWritingSongs;
private Button clearButtonHandWritingSongs;
private Button closeButtonForSongs;
private void HandWritingSearchButtonForSongs_Click(object sender, EventArgs e)
{
this.SuspendLayout();
zhuyinSearchSongButton.BackgroundImage = zhuyinSearchSongNormalBackground;
englishSearchSongButton.BackgroundImage = englishSearchSongNormalBackground;
pinyinSearchSongButton.BackgroundImage = pinyinSearchSongNormalBackground;
wordCountSearchSongButton.BackgroundImage = wordCountSearchSongNormalBackground;
handWritingSearchSongButton.BackgroundImage = handWritingSearchSongActiveBackground;
numberSearchSongButton.BackgroundImage = numberSearchSongNormalBackground;
EnableDoubleBuffering(handWritingPanelForSongs);
EnableDoubleBuffering(handwritingInputBoxForSongs);
EnableDoubleBuffering(candidateListBoxForSongs);
EnableDoubleBuffering(pictureBoxHandWritingSongs);
EnableDoubleBuffering(refillButtonHandWritingSongs);
EnableDoubleBuffering(closeButtonForSongs);
var configData = LoadConfigData();
string handWritingImagePath = Path.Combine(Application.StartupPath, configData["ImagePaths"]["HandWritingSongs"]);
ShowImageOnPictureBoxHandWritingSongs(Path.Combine(Application.StartupPath, handWritingImagePath));
// 鍵盤UI介面顯示設定
SetWordCountSongsAndButtonsVisibility(false);
SetEnglishSongsAndButtonsVisibility(false);
SetPinYinSongsAndButtonsVisibility(false);
SetHandWritingForSongsAndButtonsVisibility(true);
SetSongIDSearchAndButtonsVisibility(false);
SetZhuYinSongsAndButtonsVisibility(false);
ResetinputBox();
this.ResumeLayout();
}
private Panel handWritingPanelForSongs;
private InkOverlay inkOverlayForSongs;
private RichTextBox handwritingInputBoxForSongs;
private ListBox candidateListBoxForSongs;
private void InitializeHandWritingForSongs()
{
InitializeHandWritingPanelForSongs();
InitializeInkOverlayForSongs();
InitializeHandwritingInputBoxForSongs();
InitializeCandidateListBoxForSongs();
InitializeSpecialButtonsForHandWritingSongs();
}
private void InitializeHandWritingPanelForSongs()
{
handWritingPanelForSongs = new Panel
{
BorderStyle = BorderStyle.FixedSingle,
Visible = false
};
ResizeAndPositionControl(handWritingPanelForSongs, 366, 448, 650, 260);
this.Controls.Add(handWritingPanelForSongs);
}
private void InitializeInkOverlayForSongs()
{
try
{
inkOverlayForSongs = new InkOverlay(handWritingPanelForSongs);
inkOverlayForSongs.Enabled = false;
inkOverlayForSongs.Ink = new Ink();
inkOverlayForSongs.DefaultDrawingAttributes.Color = Color.Black;
inkOverlayForSongs.DefaultDrawingAttributes.Width = 100;
inkOverlayForSongs.Stroke += new InkCollectorStrokeEventHandler(InkOverlayForSongs_Stroke);
inkOverlayForSongs.Enabled = true;
}
catch (Exception ex)
{
Console.WriteLine("Failed to initialize ink overlay for singers: " + ex.Message);
}
}
private void InkOverlayForSongs_Stroke(object sender, InkCollectorStrokeEventArgs e)
{
RecognizeInk(inkOverlayForSongs, candidateListBoxForSongs);
}
private void InitializeHandwritingInputBoxForSongs()
{
handwritingInputBoxForSongs = new RichTextBox
{
Font = new Font("微軟正黑體", (float)26 / 900 * Screen.PrimaryScreen.Bounds.Height, FontStyle.Regular),
Visible = false
};
ResizeAndPositionControl(handwritingInputBoxForSongs, 366, 373, 541, 62);
this.Controls.Add(handwritingInputBoxForSongs);
handwritingInputBoxForSongs.TextChanged += (sender, e) =>
{
string searchText = handwritingInputBoxForSongs.Text;
var searchResults = allSongs.Where(song => song.Song.StartsWith(searchText)).ToList();
currentPage = 0;
currentSongList = searchResults;
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
};
}
private void InitializeCandidateListBoxForSongs()
{
candidateListBoxForSongs = new ListBox
{
Font = new Font("微軟正黑體", (float)26 / 900 * Screen.PrimaryScreen.Bounds.Height, FontStyle.Regular),
Visible = false
};
ResizeAndPositionControl(candidateListBoxForSongs, 350 + 679, 448, 115, 260);
candidateListBoxForSongs.SelectedIndexChanged += CandidateListBoxForSongs_SelectedIndexChanged;
this.Controls.Add(candidateListBoxForSongs);
}
private void CandidateListBoxForSongs_SelectedIndexChanged(object sender, EventArgs e)
{
if (candidateListBoxForSongs.SelectedIndex != -1)
{
string selectedWord = candidateListBoxForSongs.SelectedItem.ToString();
handwritingInputBoxForSongs.Text += selectedWord;
candidateListBoxForSongs.Visible = false;
if (inkOverlayForSongs != null)
{
inkOverlayForSongs.Ink.DeleteStrokes();
handWritingPanelForSongs.Invalidate();
}
}
}
private void ShowImageOnPictureBoxHandWritingSongs(string imagePath)
{
Bitmap originalImage = new Bitmap(imagePath);
Rectangle displayArea = new Rectangle(350, 360, 810, 360);
pictureBoxHandWritingSongs.Image = originalImage;
ResizeAndPositionPictureBox(pictureBoxHandWritingSongs, displayArea.X, displayArea.Y, displayArea.Width, displayArea.Height);
pictureBoxHandWritingSongs.Visible = true;
}
private void SetHandWritingForSongsAndButtonsVisibility(bool isVisible)
{
EnableDoubleBuffering(handWritingPanelForSongs);
EnableDoubleBuffering(handwritingInputBoxForSongs);
EnableDoubleBuffering(candidateListBoxForSongs);
EnableDoubleBuffering(pictureBoxHandWritingSongs);
EnableDoubleBuffering(refillButtonHandWritingSongs);
EnableDoubleBuffering(clearButtonHandWritingSongs);
EnableDoubleBuffering(closeButtonForSongs);
handWritingPanelForSongs.Visible = isVisible;
handwritingInputBoxForSongs.Visible = isVisible;
inkOverlayForSongs.Enabled = isVisible;
candidateListBoxForSongs.Visible = isVisible;
pictureBoxHandWritingSongs.Visible = isVisible;
refillButtonHandWritingSongs.Visible = isVisible;
clearButtonHandWritingSongs.Visible = isVisible;
closeButtonForSongs.Visible = isVisible;
if (isVisible)
{
pictureBoxHandWritingSongs.BringToFront();
handWritingPanelForSongs.BringToFront();
handwritingInputBoxForSongs.BringToFront();
candidateListBoxForSongs.BringToFront();
refillButtonHandWritingSongs.BringToFront();
clearButtonHandWritingSongs.BringToFront();
closeButtonForSongs.BringToFront();
}
}
private void InitializeSpecialButtonsForHandWritingSongs()
{
InitializeRefillButtonHandwritingSongs();
InitializeClearButtonHandWritingSongs();
InitializeCloseButtonForSongs();
}
private void InitializeRefillButtonHandwritingSongs()
{
var data = LoadConfigData();
refillButtonHandWritingCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "refillButtonHandWritingSongs");
var buttonImages = LoadButtonImages(data, "RefillButtonImagesHandWriting");
refillButtonHandWritingSongs = CreateSpecialButton(
"refillButtonHandWritingSongs",
refillButtonHandWritingCoords,
buttonImages.normal,
buttonImages.mouseOver,
buttonImages.mouseDown,
RefillButtonHandWritingSongs_Click
);
}
private void RefillButtonHandWritingSongs_Click(object sender, EventArgs e)
{
handwritingInputBoxForSongs.Text = "";
}
private void InitializeClearButtonHandWritingSongs()
{
var data = LoadConfigData();
clearButtonHandWritingCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "clearButtonHandWritingSongs");
var buttonImages = LoadButtonImages(data, "ClearButtonImagesHandWriting");
clearButtonHandWritingSongs = CreateSpecialButton(
"clearButtonHandWritingSongs",
clearButtonHandWritingCoords,
buttonImages.normal,
buttonImages.mouseOver,
buttonImages.mouseDown,
ClearButtonHandWritingSongs_Click
);
}
private void ClearButtonHandWritingSongs_Click(object sender, EventArgs e)
{
if (this.Controls.Contains(handWritingPanelForSongs) && inkOverlayForSongs != null)
{
inkOverlayForSongs.Ink.DeleteStrokes();
handWritingPanelForSongs.Invalidate();
}
}
private void InitializeCloseButtonForSongs()
{
var data = LoadConfigData();
closeButtonHandWritingCoords = LoadSpecialButtonCoordinates(data, "SpecialButtonCoordinates", "closeButtonForSongs");
var buttonImages = LoadButtonImages(data, "CloseButtonImagesHandWriting");
closeButtonForSongs = CreateSpecialButton(
"closeButtonForSongs",
closeButtonHandWritingCoords,
buttonImages.normal,
buttonImages.mouseOver,
buttonImages.mouseDown,
CloseButtonForSongs_Click
);
}
private void CloseButtonForSongs_Click(object sender, EventArgs e)
{
this.SuspendLayout();
handWritingSearchSongButton.BackgroundImage = handWritingSearchSongNormalBackground;
SetHandWritingForSongsAndButtonsVisibility(false);
this.ResumeLayout();
}
}
}

View File

@ -0,0 +1,871 @@
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;
/*
WordCountSongs -> SongIDSearch
WordCount -> SongID
numberWordCount -> numberSongID
NumberWordCount -> NumberSongID
inputBoxWordCountFontName -> inputBoxSongIDFontName
inputBoxWordCountFontSize -> inputBoxSongIDFontSize
inputBoxWordCountFontStyle -> inputBoxSongIDFontStyle
inputBoxWordCountForeColor -> inputBoxSongIDForeColor
LoadConfigDataforWordCountSongs -> LoadConfigDataforSongIDSearch
LoadButtonCoordinatesForWordCountSongs -> LoadButtonCoordinatesForSongIDSearch
LoadButtonImagesForWordCountSongs -> LoadButtonImagesForSongIDSearch
LoadSpecialButtonCoordinatesForWordCountSongs -> LoadSpecialButtonCoordinatesForSongIDSearch
RemoveWhiteBorderForWordCountSongs -> RemoveWhiteBorderForSongIDSearch
CreateSpecialButtonForWordCountSongs -> CreateSpecialButtonForSongIDSearch
LoadInputBoxConfigForWordCountSongs -> LoadInputBoxConfigForSongIDSearch
*/
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private PictureBox pictureBoxSongIDSearch;
private Button[] numberSongIDButtonsForSongs;
private Button modifyButtonSongIDSearch;
private Button clearButtonSongIDSearch;
private Button closeButtonSongIDSearch;
private string[] numberSongIDSymbols;
private (int X, int Y, int Width, int Height)[] numberSongIDButtonCoords;
private Dictionary<string, (string normal, string mouseDown, string mouseOver)> numberSongIDButtonImages;
private (int X, int Y, int Width, int Height) modifyButtonSongIDCoords;
private (int X, int Y, int Width, int Height) clearButtonSongIDCoords;
private (int X, int Y, int Width, int Height) closeButtonSongIDCoords;
private RichTextBox inputBoxSongIDSearch;
private (int X, int Y, int Width, int Height) inputBoxSongIDCoords;
private string inputBoxSongIDFontName;
private float inputBoxSongIDFontSize;
private FontStyle inputBoxSongIDFontStyle;
private Color inputBoxSongIDForeColor;
/// <summary>
/// <para> 點擊「注音歌手搜尋」按鈕時執行的事件處理函式。</para>
/// <para>此函式負責更新按鈕的背景圖片、載入對應的歌手圖片,並切換相關 UI 控件的可見性。</para>
/// </summary>
/// <param name="sender">觸發事件的物件(通常是按鈕本身)。</param>
/// <param name="e">事件參數。</param>
private void SongIDSearchSongsButton_Click(object sender, EventArgs e)
{
// 設定按鈕背景,將「注音搜尋」設為啟動狀態,其餘按鈕恢復為正常狀態
zhuyinSearchSongButton.BackgroundImage = zhuyinSearchSongNormalBackground;
englishSearchSongButton.BackgroundImage = englishSearchSongNormalBackground;
pinyinSearchSongButton.BackgroundImage = pinyinSearchSongNormalBackground;
wordCountSearchSongButton.BackgroundImage = wordCountSearchSongNormalBackground;
handWritingSearchSongButton.BackgroundImage = handWritingSearchSongNormalBackground;
numberSearchSongButton.BackgroundImage = numberSearchSongActiveBackground;
// 載入設定檔,取得圖片路徑資訊
var configData = LoadConfigDataforSongIDSearch();
// 取得「注音歌手圖片」的完整路徑
string imagePath = Path.Combine(Application.StartupPath, configData["ImagePaths"]["SongIDSearch"]);
// 在 PictureBox 中顯示對應的「注音歌手」圖片
ShowImageOnPictureBoxSongIDSearch(Path.Combine(Application.StartupPath, imagePath));
// 鍵盤UI介面顯示設定
SetWordCountSongsAndButtonsVisibility(false);
SetEnglishSongsAndButtonsVisibility(false);
SetPinYinSongsAndButtonsVisibility(false);
SetHandWritingForSongsAndButtonsVisibility(false);
SetSongIDSearchAndButtonsVisibility(true);
SetZhuYinSongsAndButtonsVisibility(false);
ResetinputBox();
SetSongIDSearchAndButtonsVisibility(true); // 顯示字數搜尋相關控件
// 顯示「注音歌手搜尋」的圖片框
pictureBoxSongIDSearch.Visible = true;
}
/// <summary>
/// <para>從 config.ini 設定檔中載入注音符號NumberWordCount Symbols。</para>
/// <para>讀取 ini 檔的 [NumberWordCountSymbols] 區塊並將「Symbols」欄位的值解析為陣列。</para>
/// </summary>
private void LoadNumberSongIDSymbolsFromConfig()
{
// 建立 INI 檔案解析器
var parser = new FileIniDataParser();
// 設定檔路徑
string iniFilePath = "config.ini";
IniData data;
// 以 UTF-8 編碼開啟並讀取 INI 檔案
using (var reader = new StreamReader(iniFilePath, Encoding.UTF8))
{
// 解析 INI 檔內容
data = parser.ReadData(reader);
}
// 取得 [NumberSongIDSymbols] 區塊中的 "Symbols" 欄位內容
string symbols = data["NumberSongIDSymbols"]["Symbols"];
// 將符號字串以逗號分隔,轉換為字串陣列
numberSongIDSymbols = symbols.Split(',');
}
/// <summary>
/// 從設定檔 (config.ini) 載入 INI 設定數據。
/// </summary>
/// <returns>回傳解析後的 INI 設定數據 (IniData)。</returns>
private IniData LoadConfigDataforSongIDSearch()
{
var parser = new FileIniDataParser();
string iniFilePath = "config.ini";
// 使用 UTF-8 讀取 INI 檔案並解析內容
using (var reader = new StreamReader(iniFilePath, Encoding.UTF8))
{
return parser.ReadData(reader);
}
}
/// <summary>
/// 從 INI 設定數據中讀取注音符號 (NumberSongID Symbols)。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <returns>回傳包含注音符號的字串陣列。</returns>
private string[] LoadNumberSongIDSymbols(IniData data)
{
// 從 INI 檔案的 [NumberSongIDSymbols] 區塊取得 Symbols 欄位值
string symbols = data["NumberSongIDSymbols"]["Symbols"];
// 以逗號分隔字串並轉換為字串陣列
return symbols.Split(',');
}
/// <summary>
/// 從 INI 設定數據中載入按鈕座標資料。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <param name="section">設定檔中按鈕座標所屬的區塊名稱。</param>
/// <param name="buttonCount">按鈕數量。</param>
/// <returns>回傳包含按鈕座標的陣列,每個元素是由 (X, Y, Width, Height) 組成的元組。</returns>
private (int X, int Y, int Width, int Height)[] LoadButtonCoordinatesForSongIDSearch(IniData data, string section, int buttonCount)
{
var buttonList = new List<(int X, int Y, int Width, int Height)>();
// 迴圈讀取每個按鈕的座標設定
for (int i = 1; i <= buttonCount; i++)
{
// 取得按鈕座標的字串 (格式: X,Y,Width,Height)
var coordString = data[section][$"button{i}"];
var coords = coordString.Split(',');
// 將座標資料轉換為 (X, Y, Width, Height) 元組並加入清單
buttonList.Add((
int.Parse(coords[0]), // X 座標
int.Parse(coords[1]), // Y 座標
int.Parse(coords[2]), // 寬度
int.Parse(coords[3]) // 高度
));
}
// 回傳所有按鈕座標的陣列
return buttonList.ToArray();
}
/// <summary>
/// 從 INI 設定數據中載入按鈕圖片檔案路徑資料 (包含正常、點擊、滑鼠移過圖片)。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <param name="section">設定檔中按鈕圖片設定所屬的區塊名稱。</param>
/// <param name="buttonCount">按鈕數量。</param>
/// <returns>回傳一個字典,鍵是按鈕名稱,值是包含正常、點擊和滑鼠移過狀態的元組。</returns>
private Dictionary<string, (string normal, string mouseDown, string mouseOver)> LoadButtonImagesForSongIDSearch(IniData data, string section, int buttonCount)
{
var buttonImages = new Dictionary<string, (string normal, string mouseDown, string mouseOver)>();
// 迴圈讀取每個按鈕的圖片設定
for (int i = 0; i < 10; i++)
{
// 讀取按鈕的三種圖片狀態:正常、點擊、滑鼠移過
buttonImages[$"button{i}"] = (
data[section][$"button{i}_normal"], // 正常狀態圖片路徑
data[section][$"button{i}_mouseDown"], // 點擊狀態圖片路徑
data[section][$"button{i}_mouseOver"] // 滑鼠移過狀態圖片路徑
);
}
// 回傳包含所有按鈕圖片路徑資料的字典
return buttonImages;
}
/// <summary>
/// 從 INI 設定數據中載入特定按鈕的座標資料。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <param name="section">設定檔中按鈕座標所屬的區塊名稱。</param>
/// <param name="buttonKey">指定按鈕的鍵名 (如 "button1")。</param>
/// <returns>回傳包含按鈕座標的元組 (X, Y, Width, Height)。</returns>
private (int X, int Y, int Width, int Height) LoadSpecialButtonCoordinatesForSongIDSearch(IniData data, string section, string buttonKey)
{
// 取得按鈕座標的字串 (格式: X,Y,Width,Height)
var coords = data[section][buttonKey].Split(',');
// 解析座標字串並回傳 (X, Y, Width, Height) 元組
return (int.Parse(coords[0]), int.Parse(coords[1]), int.Parse(coords[2]), int.Parse(coords[3]));
}
/// <summary>
/// 從 INI 設定數據中載入按鈕的圖片資料 (包含正常、點擊、滑鼠移過圖片)。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <param name="section">設定檔中按鈕圖片設定所屬的區塊名稱。</param>
/// <returns>回傳包含按鈕三種狀態圖片路徑的元組 (normal, mouseDown, mouseOver)。</returns>
private (string normal, string mouseDown, string mouseOver) LoadButtonImagesForSongIDSearch(IniData data, string section)
{
// 讀取按鈕三種狀態的圖片路徑
return (
data[section]["normal"], // 正常狀態圖片路徑
data[section]["mouseDown"], // 點擊狀態圖片路徑
data[section]["mouseOver"] // 滑鼠移過狀態圖片路徑
);
}
/// <summary>
/// 初始化並設置語音按鈕的相關資料,包括符號、座標與圖片等。
/// </summary>
private void InitializeNumberSongIDButtons()
{
// 載入配置資料
var data = LoadConfigDataforSongIDSearch();
// 載入語音符號(如拼音、注音符號等)
numberSongIDSymbols = LoadNumberSongIDSymbols(data);
// 載入按鈕座標資料
numberSongIDButtonCoords = LoadButtonCoordinatesForSongIDSearch(data, "NumberSongIDButtonCoordinates", 10);
// 載入按鈕圖片資料
numberSongIDButtonImages = LoadButtonImagesForSongIDSearch(data, "NumberSongIDButtonImages", 10);
// 初始化語音按鈕陣列,總共有 10 個按鈕
numberSongIDButtonsForSongs = new Button[10];
// 設置每個語音按鈕
for (int i = 0; i < 10; i++)
{
// 根據按鈕索引讀取其圖片資料
var buttonImages = numberSongIDButtonImages[$"button{i}"];
// 創建並初始化語音按鈕,設定其背景圖片
CreateNumberSongIDButton(i, buttonImages.normal, buttonImages.mouseDown, buttonImages.mouseOver);
}
}
/// <summary>
/// 創建一個語音按鈕,並為其設置圖片、座標、事件等屬性。
/// </summary>
/// <param name="index">按鈕的索引,用來獲取對應的語音符號、座標和圖片資料。</param>
/// <param name="normalImagePath">正常狀態下的圖片路徑。</param>
/// <param name="mouseDownImagePath">點擊狀態下的圖片路徑。</param>
/// <param name="mouseOverImagePath">滑鼠移過狀態下的圖片路徑。</param>
private void CreateNumberSongIDButton(int index, string normalImagePath, string mouseDownImagePath, string mouseOverImagePath)
{
try
{
// 創建語音按鈕並設置其屬性
numberSongIDButtonsForSongs[index] = new Button
{
Name = $"numberSongIDButton_{numberSongIDSymbols[index]}", // 按鈕名稱設為語音符號名稱
BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath)), // 設定背景圖片
BackgroundImageLayout = ImageLayout.Stretch, // 設定圖片拉伸樣式
FlatStyle = FlatStyle.Flat, // 設定為平面風格
FlatAppearance = { BorderSize = 0 } // 設定無邊框
};
// 調整按鈕大小並設置位置
ResizeAndPositionButton(numberSongIDButtonsForSongs[index], numberSongIDButtonCoords[index].X, numberSongIDButtonCoords[index].Y,
numberSongIDButtonCoords[index].Width, numberSongIDButtonCoords[index].Height);
// 從檔案中讀取正常、點擊和滑鼠懸停狀態的圖片
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));
// 設置滑鼠事件:點擊、進入、離開等,改變按鈕的背景圖片
numberSongIDButtonsForSongs[index].MouseDown += (s, e) => numberSongIDButtonsForSongs[index].BackgroundImage = mouseDownImage;
numberSongIDButtonsForSongs[index].MouseUp += (s, e) => numberSongIDButtonsForSongs[index].BackgroundImage = normalImage;
numberSongIDButtonsForSongs[index].MouseEnter += (s, e) => numberSongIDButtonsForSongs[index].BackgroundImage = mouseOverImage;
numberSongIDButtonsForSongs[index].MouseLeave += (s, e) => numberSongIDButtonsForSongs[index].BackgroundImage = normalImage;
// 設置點擊事件處理方法
numberSongIDButtonsForSongs[index].Click += NumberSongIDButton_Click;
// 設置按鈕的 Tag 屬性為對應的語音符號
numberSongIDButtonsForSongs[index].Tag = numberSongIDSymbols[index];
// 將按鈕添加到表單的控制項集合中
this.Controls.Add(numberSongIDButtonsForSongs[index]);
}
catch (Exception ex)
{
// 捕捉錯誤並輸出錯誤訊息
Console.WriteLine($"Error creating button at index {index}: {ex.Message}");
}
}
/// <summary>
/// 初始化所有與注音歌手相關的按鈕,包括語音符號按鈕、特殊按鈕及輸入框。
/// </summary>
private void InitializeButtonsForSongIDSearch()
{
// 從配置檔案加載注音符號並初始化按鈕
LoadNumberSongIDSymbolsFromConfig();
// 初始化所有語音按鈕
InitializeNumberSongIDButtons();
// 初始化注音歌手的特殊按鈕(例如音量、搜尋等)
InitializeSpecialButtonsForSongIDSearch();
// 初始化注音歌手的輸入框
InitializeInputBoxSongIDSearch();
}
/// <summary>
/// 移除圖像周圍的白色邊框,將邊框的像素透明化。
/// </summary>
/// <param name="imagePath">待處理的圖像文件路徑。</param>
/// <returns>處理後的圖像,其中白色邊框已被去除並替換為透明。</returns>
private Image RemoveWhiteBorderForSongIDSearch(string imagePath)
{
// 創建一個 Bitmap 物件來加載圖像
Bitmap bmp = new Bitmap(imagePath);
// 定義圖像的矩形區域,這是我們將要操作的區域
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
// 鎖定圖像的位圖數據,以便進行直接修改
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0; // 獲取位圖數據的起始位置
int bytes = Math.Abs(bmpData.Stride) * bmp.Height; // 計算圖像的總字節數
byte[] rgbValues = new byte[bytes]; // 用來存儲圖像的像素數據
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); // 從圖像數據中複製像素數據到 rgbValues 陣列
// 遍歷每個像素點,檢查是否為白色邊框
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x++)
{
int position = (y * bmpData.Stride) + (x * 4); // 計算當前像素的位址
byte b = rgbValues[position]; // 藍色分量
byte g = rgbValues[position + 1]; // 綠色分量
byte r = rgbValues[position + 2]; // 紅色分量
byte a = rgbValues[position + 3]; // alpha 分量(透明度)
// 如果當前像素在圖像邊緣且為白色 (255, 255, 255),則將其設為透明
if ((x < 5 || x > bmp.Width - 5 || y < 5 || y > bmp.Height - 5) && r == 255 && g == 255 && b == 255)
{
// 將白色像素的 RGB 設置為 255, 255, 255 且 alpha 設為 0 (透明)
rgbValues[position] = 255;
rgbValues[position + 1] = 255;
rgbValues[position + 2] = 255;
rgbValues[position + 3] = 0; // 透明
}
}
}
// 將修改後的像素數據重新複製回位圖數據
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// 解鎖圖像數據
bmp.UnlockBits(bmpData);
// 返回處理後的圖像
return bmp;
}
/// <summary>
/// 初始化與注音歌手相關的特殊按鈕,包括修改、清除和關閉按鈕。
/// </summary>
private void InitializeSpecialButtonsForSongIDSearch()
{
// 初始化修改按鈕
InitializeModifyButtonSongIDSearch();
// 初始化清除按鈕
InitializeClearButtonSongIDSearch();
// 初始化關閉按鈕
InitializeCloseButtonSongIDSearch();
}
/// <summary>
/// 初始化「修改」按鈕,設定按鈕的坐標、圖片和點擊事件。
/// </summary>
private void InitializeModifyButtonSongIDSearch()
{
// 加載配置數據
var data = LoadConfigDataforSongIDSearch();
// 讀取按鈕坐標
modifyButtonSongIDCoords = LoadSpecialButtonCoordinatesForSongIDSearch(data, "SpecialButtonCoordinates", "modifyButtonSongIDSearch");
// 加載按鈕圖片(正常、鼠標懸停、鼠標按下)
var buttonImages = LoadButtonImagesForSongIDSearch(data, "ModifyButtonImagesSongID");
// 創建「修改」按鈕,並設置坐標、圖片及點擊事件
modifyButtonSongIDSearch = CreateSpecialButtonForSongIDSearch(
"btnModifySongIDSearch", // 按鈕名稱
modifyButtonSongIDCoords, // 按鈕坐標
buttonImages.normal, // 正常狀態圖片
buttonImages.mouseOver, // 鼠標懸停圖片
buttonImages.mouseDown, // 鼠標按下圖片
ModifyButtonSongIDSearch_Click // 按鈕點擊事件
);
}
/// <summary>
/// 處理「修改」按鈕的點擊事件,該事件會刪除輸入框中的最後一個字符。
/// </summary>
/// <param name="sender">觸發事件的對象</param>
/// <param name="e">事件參數</param>
private void ModifyButtonSongIDSearch_Click(object sender, EventArgs e)
{
// 如果輸入框不為空,且包含輸入內容,則刪除最後一個字符
if (this.Controls.Contains(inputBoxSongIDSearch) && inputBoxSongIDSearch.Text.Length > 0)
{
inputBoxSongIDSearch.Text = inputBoxSongIDSearch.Text.Substring(0, inputBoxSongIDSearch.Text.Length - 1);
}
}
/// <summary>
/// 初始化「清除」按鈕,設定按鈕的坐標、圖片和點擊事件。
/// </summary>
private void InitializeClearButtonSongIDSearch()
{
// 加載配置數據
var data = LoadConfigDataforSongIDSearch();
// 讀取按鈕坐標
clearButtonSongIDCoords = LoadSpecialButtonCoordinatesForSongIDSearch(data, "SpecialButtonCoordinates", "clearButtonSongIDSearch");
// 加載按鈕圖片(正常、鼠標懸停、鼠標按下)
var buttonImages = LoadButtonImagesForSongIDSearch(data, "ClearButtonImagesSongID");
// 創建「清除」按鈕,並設置坐標、圖片及點擊事件
clearButtonSongIDSearch = CreateSpecialButtonForSongIDSearch(
"btnClearSongIDSearch", // 按鈕名稱
clearButtonSongIDCoords, // 按鈕坐標
buttonImages.normal, // 正常狀態圖片
buttonImages.mouseOver, // 鼠標懸停圖片
buttonImages.mouseDown, // 鼠標按下圖片
ClearButtonSongIDSearch_Click // 按鈕點擊事件
);
}
/// <summary>
/// 處理「清除」按鈕的點擊事件,該事件會清空輸入框中的所有文本。
/// </summary>
/// <param name="sender">觸發事件的對象</param>
/// <param name="e">事件參數</param>
private void ClearButtonSongIDSearch_Click(object sender, EventArgs e)
{
// 如果輸入框不為空,則清空該框的文本內容
if (this.Controls.Contains(inputBoxSongIDSearch) && inputBoxSongIDSearch.Text.Length > 0)
{
inputBoxSongIDSearch.Text = "";
}
}
/// <summary>
/// 初始化「關閉」按鈕,設定按鈕的坐標、圖片和點擊事件。
/// </summary>
private void InitializeCloseButtonSongIDSearch()
{
// 加載配置數據
var data = LoadConfigDataforSongIDSearch();
// 讀取按鈕坐標
closeButtonSongIDCoords = LoadSpecialButtonCoordinatesForSongIDSearch(data, "SpecialButtonCoordinates", "closeButtonSongIDSearch");
// 加載按鈕圖片(正常、鼠標懸停、鼠標按下)
var buttonImages = LoadButtonImagesForSongIDSearch(data, "CloseButtonImagesSongID");
// 創建「關閉」按鈕,並設置坐標、圖片及點擊事件
closeButtonSongIDSearch = CreateSpecialButtonForSongIDSearch(
"btnCloseSongIDSearch", // 按鈕名稱
closeButtonSongIDCoords, // 按鈕坐標
buttonImages.normal, // 正常狀態圖片
buttonImages.mouseOver, // 鼠標懸停圖片
buttonImages.mouseDown, // 鼠標按下圖片
CloseButtonSongIDSearch_Click // 按鈕點擊事件
);
}
/// <summary>
/// 「關閉」按鈕的點擊事件處理方法。
/// 隱藏 SongID 歌手圖片框以及與其相關的按鈕。
/// </summary>
/// <param name="sender">觸發事件的對象,這裡是關閉按鈕。</param>
/// <param name="e">事件參數。</param>
private void CloseButtonSongIDSearch_Click(object sender, EventArgs e)
{
// 隱藏 SongID 歌手圖片框
pictureBoxSongIDSearch.Visible = false;
numberSearchSongButton.BackgroundImage = numberSearchSongNormalBackground;
// 隱藏與 SongID 歌手相關的所有按鈕
SetSongIDSearchAndButtonsVisibility(false);
}
/// <summary>
/// 創建一個特殊的按鈕,並設定其顯示屬性、事件處理和位置。
/// </summary>
/// <param name="name">按鈕的名稱。</param>
/// <param name="coords">按鈕的坐標和大小,包含 X, Y, 寬度和高度。</param>
/// <param name="normalImagePath">按鈕正常狀態下的背景圖片路徑。</param>
/// <param name="mouseOverImagePath">鼠標懸停時按鈕的背景圖片路徑。</param>
/// <param name="mouseDownImagePath">鼠標點擊時按鈕的背景圖片路徑。</param>
/// <param name="clickEventHandler">按鈕的點擊事件處理程序。</param>
/// <returns>創建並返回的按鈕對象。</returns>
private Button CreateSpecialButtonForSongIDSearch(string name, (int X, int Y, int Width, int Height) coords, string normalImagePath, string mouseOverImagePath, string mouseDownImagePath, EventHandler clickEventHandler)
{
// 創建按鈕並設定基本屬性
var button = new Button
{
Name = name,
FlatStyle = FlatStyle.Flat,
FlatAppearance = { BorderSize = 0, MouseDownBackColor = Color.Transparent, MouseOverBackColor = Color.Transparent },
BackgroundImageLayout = ImageLayout.Stretch,
BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath))
};
// 設定按鈕的大小和位置
ResizeAndPositionButton(button, coords.X, coords.Y, coords.Width, coords.Height);
// 設定鼠標事件:進入、離開、按下、放開
button.MouseEnter += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, mouseOverImagePath));
button.MouseLeave += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath));
button.MouseDown += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, mouseDownImagePath));
button.MouseUp += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath));
// 註冊點擊事件處理
button.Click += clickEventHandler;
// 將按鈕添加到控件集合中
this.Controls.Add(button);
return button;
}
/// <summary>
/// 初始化 SongID 歌手的輸入框,並設定其屬性與事件處理程序。
/// </summary>
private void InitializeInputBoxSongIDSearch()
{
try
{
// 加載輸入框配置
LoadInputBoxConfigForSongIDSearch();
// 創建一個 RichTextBox 控件來作為輸入框
inputBoxSongIDSearch = new RichTextBox
{
Name = "inputBoxSongIDSearch",
ForeColor = inputBoxSongIDForeColor, // 設定文字顏色
Font = new Font(inputBoxSongIDFontName, inputBoxSongIDFontSize, inputBoxSongIDFontStyle), // 設定字體樣式
ScrollBars = RichTextBoxScrollBars.None // 不顯示滾動條
};
// 調整和定位輸入框的位置及大小
ResizeAndPositionControl(inputBoxSongIDSearch, inputBoxSongIDCoords.X, inputBoxSongIDCoords.Y, inputBoxSongIDCoords.Width, inputBoxSongIDCoords.Height);
// 設定文本變更事件,當輸入框內容改變時觸發
inputBoxSongIDSearch.TextChanged += (sender, e) =>
{
string searchText = inputBoxSongIDSearch.Text; // 取得輸入內容
// 根據輸入的注音篩選歌曲清單
var searchResults = allSongs.Where(song => song.SongNumber.StartsWith(searchText)).ToList();
// 重置分頁
currentPage = 0;
currentSongList = searchResults;
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
// 更新多頁面面板的內容
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
};
// 將輸入框加入到窗體的控件集合中
this.Controls.Add(inputBoxSongIDSearch);
}
catch (Exception ex)
{
// 如果初始化過程中出現錯誤,則在控制台輸出錯誤信息
Console.WriteLine("Error initializing inputBoxSongIDSearch: " + ex.Message);
}
}
/// <summary>
/// 從配置文件 `config.ini` 中加載輸入框的設置,包括位置、大小、字體等屬性。
/// </summary>
private void LoadInputBoxConfigForSongIDSearch()
{
try
{
// 創建 INI 解析器
var parser = new FileIniDataParser();
string iniFilePath = "config.ini"; // 配置文件的路徑
IniData data;
// 打開並讀取配置文件
using (var reader = new StreamReader(iniFilePath, Encoding.UTF8))
{
data = parser.ReadData(reader);
}
// 從配置中加載輸入框的坐標和大小
inputBoxSongIDCoords = (
int.Parse(data["InputBoxSongIDSearch"]["X"]), // 輸入框的 X 坐標
int.Parse(data["InputBoxSongIDSearch"]["Y"]), // 輸入框的 Y 坐標
int.Parse(data["InputBoxSongIDSearch"]["Width"]), // 輸入框的寬度
int.Parse(data["InputBoxSongIDSearch"]["Height"]) // 輸入框的高度
);
// 從配置中加載字體屬性
inputBoxSongIDFontName = data["InputBoxSongIDSearch"]["FontName"]; // 字體名稱
inputBoxSongIDFontSize = float.Parse(data["InputBoxSongIDSearch"]["FontSize"]); // 字體大小
inputBoxSongIDFontStyle = (FontStyle)Enum.Parse(typeof(FontStyle), data["InputBoxSongIDSearch"]["FontStyle"]); // 字體樣式
inputBoxSongIDForeColor = Color.FromName(data["InputBoxSongIDSearch"]["ForeColor"]); // 字體顏色
}
catch (Exception ex)
{
// 若發生錯誤,顯示錯誤信息
Console.WriteLine("Error loading inputBox configuration: " + ex.Message);
}
}
/// <summary>
/// 存儲 `pictureBoxSongIDSearch` 控制項的坐標和大小設置。
/// </summary>
/// <remarks>
/// 這個元組包含了 `X`、`Y` 坐標以及 `Width`、`Height` 大小,用於配置 `pictureBoxSongIDSearch` 的位置和大小。
/// </remarks>
private (int X, int Y, int Width, int Height) pictureBoxSongIDSongCoords;
/// <summary>
/// 從配置檔案中讀取 `PictureBoxSongIDSearch` 控制項的坐標和大小設置。
/// </summary>
private void LoadPictureBoxSongIDSongCoordsFromConfig()
{
var parser = new FileIniDataParser();
IniData data = parser.ReadFile("config.ini");
var coords = data["PictureBoxSongIDSearch"];
pictureBoxSongIDSongCoords = (
int.Parse(coords["X"]),
int.Parse(coords["Y"]),
int.Parse(coords["Width"]),
int.Parse(coords["Height"])
);
}
/// <summary>
/// 顯示圖片並根據配置文件設置顯示區域的大小和位置。
/// </summary>
/// <param name="imagePath">圖片的路徑。</param>
private void ShowImageOnPictureBoxSongIDSearch(string imagePath)
{
// 讀取配置文件中的顯示區域設置
LoadPictureBoxSongIDSongCoordsFromConfig();
// 加載原始圖片
Bitmap originalImage = new Bitmap(imagePath);
// 創建顯示區域,根據配置文件中的坐標和大小設置
Rectangle displayArea = new Rectangle(pictureBoxSongIDSongCoords.X, pictureBoxSongIDSongCoords.Y, pictureBoxSongIDSongCoords.Width, pictureBoxSongIDSongCoords.Height);
// 設置圖片到 PictureBox
pictureBoxSongIDSearch.Image = originalImage;
// 調整 PictureBox 大小和位置
ResizeAndPositionPictureBox(pictureBoxSongIDSearch, displayArea.X, displayArea.Y, displayArea.Width, displayArea.Height);
// 顯示圖片
pictureBoxSongIDSearch.Visible = true;
}
/// <summary>
/// 設置注音歌手相關控制項(包括圖片框、按鈕和輸入框)的顯示或隱藏狀態。
/// </summary>
/// <param name="isVisible">指定控件是否可見。True 為顯示False 為隱藏。</param>
private void SetSongIDSearchAndButtonsVisibility(bool isVisible)
{
// 定義一個動作來處理控制項的顯示或隱藏
System.Action action = () =>
{
try
{
// 暫停控制項佈局的重新排版,提高效率
SuspendLayout();
// 檢查並設置圖片框的可見性
if (pictureBoxSongIDSearch == null)
{
Console.WriteLine("pictureBoxSongIDSearch is null");
}
else
{
pictureBoxSongIDSearch.Visible = isVisible;
if (isVisible) pictureBoxSongIDSearch.BringToFront(); // 如果顯示,將其置於最前
}
// 檢查並設置拼音按鈕的可見性
if (numberSongIDButtonsForSongs == null)
{
Console.WriteLine("numberSongIDButtonsForSongs is null");
}
else
{
foreach (var button in numberSongIDButtonsForSongs)
{
if (button == null)
{
Console.WriteLine("One of the numberSongIDButtonsForSongs is null");
}
else
{
button.Visible = isVisible;
if (isVisible) button.BringToFront(); // 如果顯示,將其置於最前
}
}
}
// 檢查並設置修改按鈕的可見性
if (modifyButtonSongIDSearch == null)
{
Console.WriteLine("modifyButtonSongIDSearch is null");
}
else
{
modifyButtonSongIDSearch.Visible = isVisible;
if (isVisible) modifyButtonSongIDSearch.BringToFront(); // 如果顯示,將其置於最前
}
// 檢查並設置清除按鈕的可見性
if (clearButtonSongIDSearch == null)
{
Console.WriteLine("clearButtonSongIDSearch is null");
}
else
{
clearButtonSongIDSearch.Visible = isVisible;
if (isVisible) clearButtonSongIDSearch.BringToFront(); // 如果顯示,將其置於最前
}
// 檢查並設置關閉按鈕的可見性
if (closeButtonSongIDSearch == null)
{
Console.WriteLine("closeButtonSongIDSearch is null");
}
else
{
closeButtonSongIDSearch.Visible = isVisible;
if (isVisible) closeButtonSongIDSearch.BringToFront(); // 如果顯示,將其置於最前
}
// 檢查並設置輸入框的可見性
if (inputBoxSongIDSearch == null)
{
Console.WriteLine("inputBoxSongIDSearch is null");
}
else
{
inputBoxSongIDSearch.Visible = isVisible;
if (isVisible) inputBoxSongIDSearch.BringToFront(); // 如果顯示,將其置於最前
}
// 恢復控制項的佈局重新排版
ResumeLayout();
PerformLayout();
// 刷新所有控制項的顯示
pictureBoxSongIDSearch?.Refresh();
if (numberSongIDButtonsForSongs != null)
{
foreach (var button in numberSongIDButtonsForSongs)
{
button?.Refresh(); // 刷新每個按鈕
}
}
modifyButtonSongIDSearch?.Refresh();
clearButtonSongIDSearch?.Refresh();
closeButtonSongIDSearch?.Refresh();
inputBoxSongIDSearch?.Refresh();
}
catch (Exception ex)
{
Console.WriteLine("Error in SetSongIDSearchAndButtonsVisibility: " + ex.Message);
}
};
// 檢查是否需要在主執行緒外執行
if (this.InvokeRequired)
{
this.Invoke(action); // 如果需要,透過主執行緒執行
}
else
{
action(); // 否則直接執行
}
}
private void NumberSongIDButton_Click(object sender, EventArgs e)
{
var button = sender as Button;
if (button != null && button.Tag != null)
{
if (inputBoxSongIDSearch.Visible)
{
inputBoxSongIDSearch.Text += button.Tag.ToString();
}
}
}
}
}

View File

@ -0,0 +1,491 @@
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;
/// <summary>
/// 拼音歌曲搜尋按鈕點擊事件
/// </summary>
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;
}
/// <summary>
/// 初始化拼音按鈕
/// </summary>
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]);
}
}
/// <summary>
/// 處理拼音按鈕點擊事件
/// </summary>
/// <param name="sender">觸發事件按鈕</param>
/// <param name="e">事件參數</param>
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();
}
}
}
/// <summary>
/// 初始化拼音輸入相關的 UI 控件,包括字母按鈕、特殊功能按鈕(修改、清除、關閉),以及拼音輸入框。
/// </summary>
private void InitializeButtonsForPinYinSongs()
{
// 初始化拼音字母按鈕,根據 QWERTY 鍵盤佈局建立對應的按鈕
InitializeLetterButtonsForPinYinSongs();
// 初始化特殊功能按鈕(修改、清除、關閉)
InitializeSpecialButtonsForPinYinSongs();
// 初始化拼音輸入框,使用者可透過輸入拼音來搜尋歌曲
InitializeInputBoxPinYinSongs();
}
/// <summary>
/// 初始化拼音輸入的特殊功能按鈕,包括:
/// <para>1. 修改按鈕 - 刪除輸入框中的最後一個字母</para>
/// <para>2. 清除按鈕 - 清空輸入框的內容</para>
/// <para>3. 關閉按鈕 - 隱藏拼音輸入的 UI 元件</para>
/// </summary>
private void InitializeSpecialButtonsForPinYinSongs()
{
// 初始化「修改」按鈕(刪除輸入框最後一個字母)
InitializeModifyButtonPinYinSongs();
// 初始化「清除」按鈕(清空輸入框內容)
InitializeClearButtonPinYinSongs();
// 初始化「關閉」按鈕(關閉拼音輸入 UI
InitializeCloseButtonPinYinSongs();
}
/// <summary>
/// 初始化「修改」按鈕,提供刪除拼音輸入框最後一個字母的功能。
/// </summary>
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 // 綁定按鈕的點擊事件處理函式
);
}
/// <summary>
/// 「修改」按鈕點擊事件:刪除拼音輸入框 (inputBoxPinYinSongs) 中的最後一個字母。
/// </summary>
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);
}
}
/// <summary>
/// 初始化「清除」按鈕 (clearButtonPinYinSongs),用於清空拼音輸入框 (inputBoxPinYinSongs)。
/// </summary>
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 // 綁定按鈕的點擊事件處理函式
);
}
/// <summary>
/// 清空拼音輸入框的內容。
/// 當使用者點擊清除按鈕時,若輸入框存在且有內容,則將其清空。
/// </summary>
private void ClearButtonPinYinSongs_Click(object sender, EventArgs e)
{
// 檢查視窗內是否包含 inputBoxPinYinSongs 控制項,且輸入框內是否有文字
if (this.Controls.Contains(inputBoxPinYinSongs) && inputBoxPinYinSongs.Text.Length > 0)
{
// 清空拼音輸入框的內容
inputBoxPinYinSongs.Text = "";
}
}
/// <summary>
/// 初始化「關閉」按鈕 (closeButtonPinYinSongs),用於隱藏拼音輸入介面。
/// </summary>
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 // 綁定按鈕的點擊事件處理函式
);
}
/// <summary>
/// 關閉拼音輸入模式,隱藏相關 UI 元件。
/// </summary>
/// <param name="sender">觸發事件的按鈕。</param>
/// <param name="e">事件參數。</param>
private void CloseButtonPinYinSongs_Click(object sender, EventArgs e)
{
// 隱藏拼音輸入的背景圖片 (可能是 UI 中的輸入框背景)
pictureBoxPinYinSongs.Visible = false;
pinyinSearchSongButton.BackgroundImage = pinyinSearchSongNormalBackground;
// 設定拼音輸入框與所有相關按鈕的可見性為 false
SetPinYinSongsAndButtonsVisibility(false);
}
/// <summary>
/// 初始化拼音輸入框 (RichTextBox),並從 config.ini 讀取相關設定。
/// </summary>
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}");
}
}
/// <summary>
/// 存儲 PictureBoxPinYinSongs 的座標與尺寸信息。
/// </summary>
/// <remarks>
/// 此元組包含以下四個值:
/// XX 座標
/// , YY 座標
/// , Width寬度
/// , Height高度
/// </remarks>
private (int X, int Y, int Width, int Height) pictureBoxPinYinSongCoords;
/// <summary>
/// 從 config.ini 配置檔案中載入 PictureBoxPinYinSongs 的座標與尺寸設定。
/// </summary>
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"]) // 解析 高度
);
}
/// <summary>
/// 顯示拼音歌曲圖片
/// </summary>
/// <param name="imagePath">圖片路徑</param>
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;
}
/// <summary>
/// 設定拼音模式的 UI 是否可見
/// </summary>
/// <param name="isVisible">是否可見</param>
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();
}
}
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using Microsoft.Ink;
namespace DualScreenDemo
{
public partial class PrimaryForm
{
}
}

View File

@ -0,0 +1,876 @@
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;
/*
ZhuYinSingers -> WordCountSongs
ZhuYin -> WordCount
phonetic -> numberWordCount
Phonetic -> NumberWordCount
inputBoxFontName -> inputBoxWordCountFontName
inputBoxFontSize -> inputBoxWordCountFontSize
inputBoxFontStyle -> inputBoxWordCountFontStyle
inputBoxForeColor -> inputBoxWordCountForeColor
LoadConfigData -> LoadConfigDataforWordCountSongs
LoadButtonCoordinates -> LoadButtonCoordinatesForWordCountSongs
LoadButtonImages -> LoadButtonImagesForWordCountSongs
LoadSpecialButtonCoordinates -> LoadSpecialButtonCoordinatesForWordCountSongs
RemoveWhiteBorder -> RemoveWhiteBorderForWordCountSongs
CreateSpecialButton -> CreateSpecialButtonForWordCountSongs
LoadInputBoxConfig -> LoadInputBoxConfigForWordCountSongs
*/
namespace DualScreenDemo
{
public partial class PrimaryForm
{
private PictureBox pictureBoxWordCountSongs;
private Button[] numberWordCountButtonsForSongs;
private Button modifyButtonWordCountSongs;
private Button clearButtonWordCountSongs;
private Button closeButtonWordCountSongs;
//private string[] numberWordCountSymbols;
//private (int X, int Y, int Width, int Height)[] numberWordCountButtonCoords;
//private Dictionary<string, (string normal, string mouseDown, string mouseOver)> numberWordCountButtonImages;
//private (int X, int Y, int Width, int Height) modifyButtonWordCountCoords;
//private (int X, int Y, int Width, int Height) clearButtonWordCountCoords;
//private (int X, int Y, int Width, int Height) closeButtonWordCountCoords;
private RichTextBox inputBoxWordCountSongs;
//private (int X, int Y, int Width, int Height) inputBoxWordCountCoords;
//private string inputBoxWordCountFontName;
//private float inputBoxWordCountFontSize;
//private FontStyle inputBoxWordCountFontStyle;
//private Color inputBoxWordCountForeColor;
/// <summary>
/// <para> 點擊「注音歌手搜尋」按鈕時執行的事件處理函式。</para>
/// <para>此函式負責更新按鈕的背景圖片、載入對應的歌手圖片,並切換相關 UI 控件的可見性。</para>
/// </summary>
/// <param name="sender">觸發事件的物件(通常是按鈕本身)。</param>
/// <param name="e">事件參數。</param>
private void WordCountSearchSongsButton_Click(object sender, EventArgs e)
{
// 設定按鈕背景,將「注音搜尋」設為啟動狀態,其餘按鈕恢復為正常狀態
zhuyinSearchSongButton.BackgroundImage = zhuyinSearchSongNormalBackground;
englishSearchSongButton.BackgroundImage = englishSearchSongNormalBackground;
pinyinSearchSongButton.BackgroundImage = pinyinSearchSongNormalBackground;
wordCountSearchSongButton.BackgroundImage = wordCountSearchSongActiveBackground;
handWritingSearchSongButton.BackgroundImage = handWritingSearchSongNormalBackground;
numberSearchSongButton.BackgroundImage = numberSearchSongNormalBackground;
// 載入設定檔,取得圖片路徑資訊
var configData = LoadConfigDataforWordCountSongs();
// 取得「注音歌手圖片」的完整路徑
string imagePath = Path.Combine(Application.StartupPath, configData["ImagePaths"]["WordCountSongs"]);
// 在 PictureBox 中顯示對應的「注音歌手」圖片
ShowImageOnPictureBoxWordCountSongs(Path.Combine(Application.StartupPath, imagePath));
// 鍵盤UI介面顯示設定
SetWordCountSongsAndButtonsVisibility(true);
SetEnglishSongsAndButtonsVisibility(false);
SetPinYinSongsAndButtonsVisibility(false);
SetHandWritingForSongsAndButtonsVisibility(false);
SetSongIDSearchAndButtonsVisibility(false);
SetZhuYinSongsAndButtonsVisibility(false);
SetWordCountSongsAndButtonsVisibility(true); // 顯示字數搜尋相關控件
// 顯示「注音歌手搜尋」的圖片框
ResetinputBox();
pictureBoxWordCountSongs.Visible = true;
}
/// <summary>
/// <para>從 config.ini 設定檔中載入注音符號NumberWordCount Symbols。</para>
/// <para>讀取 ini 檔的 [NumberWordCountSymbols] 區塊並將「Symbols」欄位的值解析為陣列。</para>
/// </summary>
/*private void LoadNumberWordCountSymbolsFromConfig()
{
// 建立 INI 檔案解析器
var parser = new FileIniDataParser();
// 設定檔路徑
string iniFilePath = "config.ini";
IniData data;
// 以 UTF-8 編碼開啟並讀取 INI 檔案
using (var reader = new StreamReader(iniFilePath, Encoding.UTF8))
{
// 解析 INI 檔內容
data = parser.ReadData(reader);
}
// 取得 [NumberWordCountSymbols] 區塊中的 "Symbols" 欄位內容
string symbols = data["NumberWordCountSymbols"]["Symbols"];
// 將符號字串以逗號分隔,轉換為字串陣列
numberWordCountSymbols = symbols.Split(',');
}*/
/// <summary>
/// 從設定檔 (config.ini) 載入 INI 設定數據。
/// </summary>
/// <returns>回傳解析後的 INI 設定數據 (IniData)。</returns>
private IniData LoadConfigDataforWordCountSongs()
{
var parser = new FileIniDataParser();
string iniFilePath = "config.ini";
// 使用 UTF-8 讀取 INI 檔案並解析內容
using (var reader = new StreamReader(iniFilePath, Encoding.UTF8))
{
return parser.ReadData(reader);
}
}
/// <summary>
/// 從 INI 設定數據中讀取注音符號 (NumberWordCount Symbols)。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <returns>回傳包含注音符號的字串陣列。</returns>
/*private string[] LoadNumberWordCountSymbols(IniData data)
{
// 從 INI 檔案的 [NumberWordCountSymbols] 區塊取得 Symbols 欄位值
string symbols = data["NumberWordCountSymbols"]["Symbols"];
// 以逗號分隔字串並轉換為字串陣列
return symbols.Split(',');
}*/
/// <summary>
/// 從 INI 設定數據中載入按鈕座標資料。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <param name="section">設定檔中按鈕座標所屬的區塊名稱。</param>
/// <param name="buttonCount">按鈕數量。</param>
/// <returns>回傳包含按鈕座標的陣列,每個元素是由 (X, Y, Width, Height) 組成的元組。</returns>
private (int X, int Y, int Width, int Height)[] LoadButtonCoordinatesForWordCountSongs(IniData data, string section, int buttonCount)
{
var buttonList = new List<(int X, int Y, int Width, int Height)>();
// 迴圈讀取每個按鈕的座標設定
for (int i = 1; i <= buttonCount; i++)
{
// 取得按鈕座標的字串 (格式: X,Y,Width,Height)
var coordString = data[section][$"button{i}"];
var coords = coordString.Split(',');
// 將座標資料轉換為 (X, Y, Width, Height) 元組並加入清單
buttonList.Add((
int.Parse(coords[0]), // X 座標
int.Parse(coords[1]), // Y 座標
int.Parse(coords[2]), // 寬度
int.Parse(coords[3]) // 高度
));
}
// 回傳所有按鈕座標的陣列
return buttonList.ToArray();
}
/// <summary>
/// 從 INI 設定數據中載入按鈕圖片檔案路徑資料 (包含正常、點擊、滑鼠移過圖片)。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <param name="section">設定檔中按鈕圖片設定所屬的區塊名稱。</param>
/// <param name="buttonCount">按鈕數量。</param>
/// <returns>回傳一個字典,鍵是按鈕名稱,值是包含正常、點擊和滑鼠移過狀態的元組。</returns>
private Dictionary<string, (string normal, string mouseDown, string mouseOver)> LoadButtonImagesForWordCountSongs(IniData data, string section, int buttonCount)
{
var buttonImages = new Dictionary<string, (string normal, string mouseDown, string mouseOver)>();
// 迴圈讀取每個按鈕的圖片設定
for (int i = 0; i < 10; i++)
{
// 讀取按鈕的三種圖片狀態:正常、點擊、滑鼠移過
buttonImages[$"button{i}"] = (
data[section][$"button{i}_normal"], // 正常狀態圖片路徑
data[section][$"button{i}_mouseDown"], // 點擊狀態圖片路徑
data[section][$"button{i}_mouseOver"] // 滑鼠移過狀態圖片路徑
);
}
// 回傳包含所有按鈕圖片路徑資料的字典
return buttonImages;
}
/// <summary>
/// 從 INI 設定數據中載入特定按鈕的座標資料。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <param name="section">設定檔中按鈕座標所屬的區塊名稱。</param>
/// <param name="buttonKey">指定按鈕的鍵名 (如 "button1")。</param>
/// <returns>回傳包含按鈕座標的元組 (X, Y, Width, Height)。</returns>
private (int X, int Y, int Width, int Height) LoadSpecialButtonCoordinatesForWordCountSongs(IniData data, string section, string buttonKey)
{
// 取得按鈕座標的字串 (格式: X,Y,Width,Height)
var coords = data[section][buttonKey].Split(',');
// 解析座標字串並回傳 (X, Y, Width, Height) 元組
return (int.Parse(coords[0]), int.Parse(coords[1]), int.Parse(coords[2]), int.Parse(coords[3]));
}
/// <summary>
/// 從 INI 設定數據中載入按鈕的圖片資料 (包含正常、點擊、滑鼠移過圖片)。
/// </summary>
/// <param name="data">已解析的 INI 設定數據。</param>
/// <param name="section">設定檔中按鈕圖片設定所屬的區塊名稱。</param>
/// <returns>回傳包含按鈕三種狀態圖片路徑的元組 (normal, mouseDown, mouseOver)。</returns>
private (string normal, string mouseDown, string mouseOver) LoadButtonImagesForWordCountSongs(IniData data, string section)
{
// 讀取按鈕三種狀態的圖片路徑
return (
data[section]["normal"], // 正常狀態圖片路徑
data[section]["mouseDown"], // 點擊狀態圖片路徑
data[section]["mouseOver"] // 滑鼠移過狀態圖片路徑
);
}
/// <summary>
/// 初始化並設置語音按鈕的相關資料,包括符號、座標與圖片等。
/// </summary>
private void InitializeNumberWordCountSongsButtons()
{
// 載入配置資料
var data = LoadConfigDataforWordCountSongs();
// 載入語音符號(如拼音、注音符號等)
numberWordCountSymbols = LoadNumberWordCountSymbols(data);
// 載入按鈕座標資料
numberWordCountButtonCoords = LoadButtonCoordinatesForWordCountSongs(data, "NumberWordCountButtonCoordinates", 10);
// 載入按鈕圖片資料
numberWordCountButtonImages = LoadButtonImagesForWordCountSongs(data, "NumberWordCountButtonImages", 10);
// 初始化語音按鈕陣列,總共有 10 個按鈕
numberWordCountButtonsForSongs = new Button[10];
// 設置每個語音按鈕
for (int i = 0; i < 10; i++)
{
// 根據按鈕索引讀取其圖片資料
var buttonImages = numberWordCountButtonImages[$"button{i}"];
// 創建並初始化語音按鈕,設定其背景圖片
CreateNumberWordCountSongsButton(i, buttonImages.normal, buttonImages.mouseDown, buttonImages.mouseOver);
}
}
/// <summary>
/// 創建一個語音按鈕,並為其設置圖片、座標、事件等屬性。
/// </summary>
/// <param name="index">按鈕的索引,用來獲取對應的語音符號、座標和圖片資料。</param>
/// <param name="normalImagePath">正常狀態下的圖片路徑。</param>
/// <param name="mouseDownImagePath">點擊狀態下的圖片路徑。</param>
/// <param name="mouseOverImagePath">滑鼠移過狀態下的圖片路徑。</param>
private void CreateNumberWordCountSongsButton(int index, string normalImagePath, string mouseDownImagePath, string mouseOverImagePath)
{
try
{
// 創建語音按鈕並設置其屬性
numberWordCountButtonsForSongs[index] = new Button
{
Name = $"numberWordCountButton_{numberWordCountSymbols[index]}", // 按鈕名稱設為語音符號名稱
BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath)), // 設定背景圖片
BackgroundImageLayout = ImageLayout.Stretch, // 設定圖片拉伸樣式
FlatStyle = FlatStyle.Flat, // 設定為平面風格
FlatAppearance = { BorderSize = 0 } // 設定無邊框
};
// 調整按鈕大小並設置位置
ResizeAndPositionButton(numberWordCountButtonsForSongs[index], numberWordCountButtonCoords[index].X, numberWordCountButtonCoords[index].Y,
numberWordCountButtonCoords[index].Width, numberWordCountButtonCoords[index].Height);
// 從檔案中讀取正常、點擊和滑鼠懸停狀態的圖片
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));
// 設置滑鼠事件:點擊、進入、離開等,改變按鈕的背景圖片
numberWordCountButtonsForSongs[index].MouseDown += (s, e) => numberWordCountButtonsForSongs[index].BackgroundImage = mouseDownImage;
numberWordCountButtonsForSongs[index].MouseUp += (s, e) => numberWordCountButtonsForSongs[index].BackgroundImage = normalImage;
numberWordCountButtonsForSongs[index].MouseEnter += (s, e) => numberWordCountButtonsForSongs[index].BackgroundImage = mouseOverImage;
numberWordCountButtonsForSongs[index].MouseLeave += (s, e) => numberWordCountButtonsForSongs[index].BackgroundImage = normalImage;
// 設置點擊事件處理方法
numberWordCountButtonsForSongs[index].Click += NumberWordCountSongsButton_Click;
// 設置按鈕的 Tag 屬性為對應的語音符號
numberWordCountButtonsForSongs[index].Tag = numberWordCountSymbols[index];
// 將按鈕添加到表單的控制項集合中
this.Controls.Add(numberWordCountButtonsForSongs[index]);
}
catch (Exception ex)
{
// 捕捉錯誤並輸出錯誤訊息
Console.WriteLine($"Error creating button at index {index}: {ex.Message}");
}
}
/// <summary>
/// 初始化所有與注音歌手相關的按鈕,包括語音符號按鈕、特殊按鈕及輸入框。
/// </summary>
private void InitializeButtonsForWordCountSongs()
{
// 從配置檔案加載注音符號並初始化按鈕
LoadNumberWordCountSymbolsFromConfig();
// 初始化所有語音按鈕
InitializeNumberWordCountSongsButtons();
// 初始化注音歌手的特殊按鈕(例如音量、搜尋等)
InitializeSpecialButtonsForWordCountSongs();
// 初始化注音歌手的輸入框
InitializeInputBoxWordCountSongs();
}
/// <summary>
/// 移除圖像周圍的白色邊框,將邊框的像素透明化。
/// </summary>
/// <param name="imagePath">待處理的圖像文件路徑。</param>
/// <returns>處理後的圖像,其中白色邊框已被去除並替換為透明。</returns>
private Image RemoveWhiteBorderForWordCountSongs(string imagePath)
{
// 創建一個 Bitmap 物件來加載圖像
Bitmap bmp = new Bitmap(imagePath);
// 定義圖像的矩形區域,這是我們將要操作的區域
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
// 鎖定圖像的位圖數據,以便進行直接修改
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0; // 獲取位圖數據的起始位置
int bytes = Math.Abs(bmpData.Stride) * bmp.Height; // 計算圖像的總字節數
byte[] rgbValues = new byte[bytes]; // 用來存儲圖像的像素數據
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); // 從圖像數據中複製像素數據到 rgbValues 陣列
// 遍歷每個像素點,檢查是否為白色邊框
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x++)
{
int position = (y * bmpData.Stride) + (x * 4); // 計算當前像素的位址
byte b = rgbValues[position]; // 藍色分量
byte g = rgbValues[position + 1]; // 綠色分量
byte r = rgbValues[position + 2]; // 紅色分量
byte a = rgbValues[position + 3]; // alpha 分量(透明度)
// 如果當前像素在圖像邊緣且為白色 (255, 255, 255),則將其設為透明
if ((x < 5 || x > bmp.Width - 5 || y < 5 || y > bmp.Height - 5) && r == 255 && g == 255 && b == 255)
{
// 將白色像素的 RGB 設置為 255, 255, 255 且 alpha 設為 0 (透明)
rgbValues[position] = 255;
rgbValues[position + 1] = 255;
rgbValues[position + 2] = 255;
rgbValues[position + 3] = 0; // 透明
}
}
}
// 將修改後的像素數據重新複製回位圖數據
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// 解鎖圖像數據
bmp.UnlockBits(bmpData);
// 返回處理後的圖像
return bmp;
}
/// <summary>
/// 初始化與注音歌手相關的特殊按鈕,包括修改、清除和關閉按鈕。
/// </summary>
private void InitializeSpecialButtonsForWordCountSongs()
{
// 初始化修改按鈕
InitializeModifyButtonWordCountSongs();
// 初始化清除按鈕
InitializeClearButtonWordCountSongs();
// 初始化關閉按鈕
InitializeCloseButtonWordCountSongs();
}
/// <summary>
/// 初始化「修改」按鈕,設定按鈕的坐標、圖片和點擊事件。
/// </summary>
private void InitializeModifyButtonWordCountSongs()
{
// 加載配置數據
var data = LoadConfigDataforWordCountSongs();
// 讀取按鈕坐標
modifyButtonWordCountCoords = LoadSpecialButtonCoordinatesForWordCountSongs(data, "SpecialButtonCoordinates", "modifyButtonWordCountSongs");
// 加載按鈕圖片(正常、鼠標懸停、鼠標按下)
var buttonImages = LoadButtonImagesForWordCountSongs(data, "ModifyButtonImagesWordCount");
// 創建「修改」按鈕,並設置坐標、圖片及點擊事件
modifyButtonWordCountSongs = CreateSpecialButtonForWordCountSongs(
"btnModifyWordCountSongs", // 按鈕名稱
modifyButtonWordCountCoords, // 按鈕坐標
buttonImages.normal, // 正常狀態圖片
buttonImages.mouseOver, // 鼠標懸停圖片
buttonImages.mouseDown, // 鼠標按下圖片
ModifyButtonWordCountSongs_Click // 按鈕點擊事件
);
}
/// <summary>
/// 處理「修改」按鈕的點擊事件,該事件會刪除輸入框中的最後一個字符。
/// </summary>
/// <param name="sender">觸發事件的對象</param>
/// <param name="e">事件參數</param>
private void ModifyButtonWordCountSongs_Click(object sender, EventArgs e)
{
// 如果輸入框不為空,且包含輸入內容,則刪除最後一個字符
if (this.Controls.Contains(inputBoxWordCountSongs) && inputBoxWordCountSongs.Text.Length > 0)
{
inputBoxWordCountSongs.Text = inputBoxWordCountSongs.Text.Substring(0, inputBoxWordCountSongs.Text.Length - 1);
}
}
/// <summary>
/// 初始化「清除」按鈕,設定按鈕的坐標、圖片和點擊事件。
/// </summary>
private void InitializeClearButtonWordCountSongs()
{
// 加載配置數據
var data = LoadConfigDataforWordCountSongs();
// 讀取按鈕坐標
clearButtonWordCountCoords = LoadSpecialButtonCoordinatesForWordCountSongs(data, "SpecialButtonCoordinates", "clearButtonWordCountSongs");
// 加載按鈕圖片(正常、鼠標懸停、鼠標按下)
var buttonImages = LoadButtonImagesForWordCountSongs(data, "ClearButtonImagesWordCount");
// 創建「清除」按鈕,並設置坐標、圖片及點擊事件
clearButtonWordCountSongs = CreateSpecialButtonForWordCountSongs(
"btnClearWordCountSongs", // 按鈕名稱
clearButtonWordCountCoords, // 按鈕坐標
buttonImages.normal, // 正常狀態圖片
buttonImages.mouseOver, // 鼠標懸停圖片
buttonImages.mouseDown, // 鼠標按下圖片
ClearButtonWordCountSongs_Click // 按鈕點擊事件
);
}
/// <summary>
/// 處理「清除」按鈕的點擊事件,該事件會清空輸入框中的所有文本。
/// </summary>
/// <param name="sender">觸發事件的對象</param>
/// <param name="e">事件參數</param>
private void ClearButtonWordCountSongs_Click(object sender, EventArgs e)
{
// 如果輸入框不為空,則清空該框的文本內容
if (this.Controls.Contains(inputBoxWordCountSongs) && inputBoxWordCountSongs.Text.Length > 0)
{
inputBoxWordCountSongs.Text = "";
}
}
/// <summary>
/// 初始化「關閉」按鈕,設定按鈕的坐標、圖片和點擊事件。
/// </summary>
private void InitializeCloseButtonWordCountSongs()
{
// 加載配置數據
var data = LoadConfigDataforWordCountSongs();
// 讀取按鈕坐標
closeButtonWordCountCoords = LoadSpecialButtonCoordinatesForWordCountSongs(data, "SpecialButtonCoordinates", "closeButtonWordCountSongs");
// 加載按鈕圖片(正常、鼠標懸停、鼠標按下)
var buttonImages = LoadButtonImagesForWordCountSongs(data, "CloseButtonImagesWordCount");
// 創建「關閉」按鈕,並設置坐標、圖片及點擊事件
closeButtonWordCountSongs = CreateSpecialButtonForWordCountSongs(
"btnCloseWordCountSongs", // 按鈕名稱
closeButtonWordCountCoords, // 按鈕坐標
buttonImages.normal, // 正常狀態圖片
buttonImages.mouseOver, // 鼠標懸停圖片
buttonImages.mouseDown, // 鼠標按下圖片
CloseButtonWordCountSongs_Click // 按鈕點擊事件
);
}
/// <summary>
/// 「關閉」按鈕的點擊事件處理方法。
/// 隱藏 WordCount 歌手圖片框以及與其相關的按鈕。
/// </summary>
/// <param name="sender">觸發事件的對象,這裡是關閉按鈕。</param>
/// <param name="e">事件參數。</param>
private void CloseButtonWordCountSongs_Click(object sender, EventArgs e)
{
// 隱藏 WordCount 歌手圖片框
pictureBoxWordCountSongs.Visible = false;
// 關閉背景圖式
wordCountSearchSongButton.BackgroundImage = wordCountSearchSongNormalBackground;
// 隱藏與 WordCount 歌手相關的所有按鈕
SetWordCountSongsAndButtonsVisibility(false);
}
/// <summary>
/// 創建一個特殊的按鈕,並設定其顯示屬性、事件處理和位置。
/// </summary>
/// <param name="name">按鈕的名稱。</param>
/// <param name="coords">按鈕的坐標和大小,包含 X, Y, 寬度和高度。</param>
/// <param name="normalImagePath">按鈕正常狀態下的背景圖片路徑。</param>
/// <param name="mouseOverImagePath">鼠標懸停時按鈕的背景圖片路徑。</param>
/// <param name="mouseDownImagePath">鼠標點擊時按鈕的背景圖片路徑。</param>
/// <param name="clickEventHandler">按鈕的點擊事件處理程序。</param>
/// <returns>創建並返回的按鈕對象。</returns>
private Button CreateSpecialButtonForWordCountSongs(string name, (int X, int Y, int Width, int Height) coords, string normalImagePath, string mouseOverImagePath, string mouseDownImagePath, EventHandler clickEventHandler)
{
// 創建按鈕並設定基本屬性
var button = new Button
{
Name = name,
FlatStyle = FlatStyle.Flat,
FlatAppearance = { BorderSize = 0, MouseDownBackColor = Color.Transparent, MouseOverBackColor = Color.Transparent },
BackgroundImageLayout = ImageLayout.Stretch,
BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath))
};
// 設定按鈕的大小和位置
ResizeAndPositionButton(button, coords.X, coords.Y, coords.Width, coords.Height);
// 設定鼠標事件:進入、離開、按下、放開
button.MouseEnter += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, mouseOverImagePath));
button.MouseLeave += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath));
button.MouseDown += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, mouseDownImagePath));
button.MouseUp += (sender, e) => button.BackgroundImage = Image.FromFile(Path.Combine(Application.StartupPath, normalImagePath));
// 註冊點擊事件處理
button.Click += clickEventHandler;
// 將按鈕添加到控件集合中
this.Controls.Add(button);
return button;
}
/// <summary>
/// 初始化 WordCount 歌手的輸入框,並設定其屬性與事件處理程序。
/// </summary>
private void InitializeInputBoxWordCountSongs()
{
try
{
// 加載輸入框配置
LoadInputBoxConfigForWordCountSongs();
// 創建一個 RichTextBox 控件來作為輸入框
inputBoxWordCountSongs = new RichTextBox
{
Name = "inputBoxWordCountSongs",
ForeColor = inputBoxWordCountForeColor, // 設定文字顏色
Font = new Font(inputBoxWordCountFontName, inputBoxWordCountFontSize, inputBoxWordCountFontStyle), // 設定字體樣式
ScrollBars = RichTextBoxScrollBars.None // 不顯示滾動條
};
// 調整和定位輸入框的位置及大小
ResizeAndPositionControl(inputBoxWordCountSongs, inputBoxWordCountCoords.X, inputBoxWordCountCoords.Y, inputBoxWordCountCoords.Width, inputBoxWordCountCoords.Height);
// 設定文本變更事件,當輸入框內容改變時觸發
inputBoxWordCountSongs.TextChanged += (sender, e) =>
{
string searchText = inputBoxWordCountSongs.Text; // 取得輸入內容
if (int.TryParse(searchText, out int length))
{
// 顯示搜尋結果
var searchResults = allSongs
.Where(song => song.Song.Length == length)
.ToList();
// 重置分頁
currentPage = 0;
currentSongList = searchResults;
totalPages = (int)Math.Ceiling((double)searchResults.Count / itemsPerPage);
// 更新多頁面面板的內容
multiPagePanel.currentPageIndex = 0;
multiPagePanel.LoadSongs(currentSongList);
}
};
// 將輸入框加入到窗體的控件集合中
this.Controls.Add(inputBoxWordCountSongs);
}
catch (Exception ex)
{
// 如果初始化過程中出現錯誤,則在控制台輸出錯誤信息
Console.WriteLine("Error initializing inputBoxWordCountSongs: " + ex.Message);
}
}
/// <summary>
/// 從配置文件 `config.ini` 中加載輸入框的設置,包括位置、大小、字體等屬性。
/// </summary>
private void LoadInputBoxConfigForWordCountSongs()
{
try
{
// 創建 INI 解析器
var parser = new FileIniDataParser();
string iniFilePath = "config.ini"; // 配置文件的路徑
IniData data;
// 打開並讀取配置文件
using (var reader = new StreamReader(iniFilePath, Encoding.UTF8))
{
data = parser.ReadData(reader);
}
// 從配置中加載輸入框的坐標和大小
inputBoxWordCountCoords = (
int.Parse(data["InputBoxWordCountSongs"]["X"]), // 輸入框的 X 坐標
int.Parse(data["InputBoxWordCountSongs"]["Y"]), // 輸入框的 Y 坐標
int.Parse(data["InputBoxWordCountSongs"]["Width"]), // 輸入框的寬度
int.Parse(data["InputBoxWordCountSongs"]["Height"]) // 輸入框的高度
);
// 從配置中加載字體屬性
inputBoxWordCountFontName = data["InputBoxWordCountSongs"]["FontName"]; // 字體名稱
inputBoxWordCountFontSize = float.Parse(data["InputBoxWordCountSongs"]["FontSize"]); // 字體大小
inputBoxWordCountFontStyle = (FontStyle)Enum.Parse(typeof(FontStyle), data["InputBoxWordCountSongs"]["FontStyle"]); // 字體樣式
inputBoxWordCountForeColor = Color.FromName(data["InputBoxWordCountSongs"]["ForeColor"]); // 字體顏色
}
catch (Exception ex)
{
// 若發生錯誤,顯示錯誤信息
Console.WriteLine("Error loading inputBox configuration: " + ex.Message);
}
}
/// <summary>
/// 存儲 `pictureBoxWordCountSongs` 控制項的坐標和大小設置。
/// </summary>
/// <remarks>
/// 這個元組包含了 `X`、`Y` 坐標以及 `Width`、`Height` 大小,用於配置 `pictureBoxWordCountSongs` 的位置和大小。
/// </remarks>
private (int X, int Y, int Width, int Height) pictureBoxWordCountSongCoords;
/// <summary>
/// 從配置檔案中讀取 `PictureBoxWordCountSongs` 控制項的坐標和大小設置。
/// </summary>
private void LoadPictureBoxWordCountSongCoordsFromConfig()
{
var parser = new FileIniDataParser();
IniData data = parser.ReadFile("config.ini");
var coords = data["PictureBoxWordCountSongs"];
pictureBoxWordCountSongCoords = (
int.Parse(coords["X"]),
int.Parse(coords["Y"]),
int.Parse(coords["Width"]),
int.Parse(coords["Height"])
);
}
/// <summary>
/// 顯示圖片並根據配置文件設置顯示區域的大小和位置。
/// </summary>
/// <param name="imagePath">圖片的路徑。</param>
private void ShowImageOnPictureBoxWordCountSongs(string imagePath)
{
// 讀取配置文件中的顯示區域設置
LoadPictureBoxWordCountSongCoordsFromConfig();
// 加載原始圖片
Bitmap originalImage = new Bitmap(imagePath);
// 創建顯示區域,根據配置文件中的坐標和大小設置
Rectangle displayArea = new Rectangle(pictureBoxWordCountSongCoords.X, pictureBoxWordCountSongCoords.Y, pictureBoxWordCountSongCoords.Width, pictureBoxWordCountSongCoords.Height);
// 設置圖片到 PictureBox
pictureBoxWordCountSongs.Image = originalImage;
// 調整 PictureBox 大小和位置
ResizeAndPositionPictureBox(pictureBoxWordCountSongs, displayArea.X, displayArea.Y, displayArea.Width, displayArea.Height);
// 顯示圖片
pictureBoxWordCountSongs.Visible = true;
}
/// <summary>
/// 設置注音歌手相關控制項(包括圖片框、按鈕和輸入框)的顯示或隱藏狀態。
/// </summary>
/// <param name="isVisible">指定控件是否可見。True 為顯示False 為隱藏。</param>
private void SetWordCountSongsAndButtonsVisibility(bool isVisible)
{
// 定義一個動作來處理控制項的顯示或隱藏
System.Action action = () =>
{
try
{
// 暫停控制項佈局的重新排版,提高效率
SuspendLayout();
// 檢查並設置圖片框的可見性
if (pictureBoxWordCountSongs == null)
{
Console.WriteLine("pictureBoxWordCountSongs is null");
}
else
{
pictureBoxWordCountSongs.Visible = isVisible;
if (isVisible) pictureBoxWordCountSongs.BringToFront(); // 如果顯示,將其置於最前
}
// 檢查並設置拼音按鈕的可見性
if (numberWordCountButtonsForSongs == null)
{
Console.WriteLine("numberWordCountButtonsForSongs is null");
}
else
{
foreach (var button in numberWordCountButtonsForSongs)
{
if (button == null)
{
Console.WriteLine("One of the numberWordCountButtonsForSongs is null");
}
else
{
button.Visible = isVisible;
if (isVisible) button.BringToFront(); // 如果顯示,將其置於最前
}
}
}
// 檢查並設置修改按鈕的可見性
if (modifyButtonWordCountSongs == null)
{
Console.WriteLine("modifyButtonWordCountSongs is null");
}
else
{
modifyButtonWordCountSongs.Visible = isVisible;
if (isVisible) modifyButtonWordCountSongs.BringToFront(); // 如果顯示,將其置於最前
}
// 檢查並設置清除按鈕的可見性
if (clearButtonWordCountSongs == null)
{
Console.WriteLine("clearButtonWordCountSongs is null");
}
else
{
clearButtonWordCountSongs.Visible = isVisible;
if (isVisible) clearButtonWordCountSongs.BringToFront(); // 如果顯示,將其置於最前
}
// 檢查並設置關閉按鈕的可見性
if (closeButtonWordCountSongs == null)
{
Console.WriteLine("closeButtonWordCountSongs is null");
}
else
{
closeButtonWordCountSongs.Visible = isVisible;
if (isVisible) closeButtonWordCountSongs.BringToFront(); // 如果顯示,將其置於最前
}
// 檢查並設置輸入框的可見性
if (inputBoxWordCountSongs == null)
{
Console.WriteLine("inputBoxWordCountSongs is null");
}
else
{
inputBoxWordCountSongs.Visible = isVisible;
if (isVisible) inputBoxWordCountSongs.BringToFront(); // 如果顯示,將其置於最前
}
// 恢復控制項的佈局重新排版
ResumeLayout();
PerformLayout();
// 刷新所有控制項的顯示
pictureBoxWordCountSongs?.Refresh();
if (numberWordCountButtonsForSongs != null)
{
foreach (var button in numberWordCountButtonsForSongs)
{
button?.Refresh(); // 刷新每個按鈕
}
}
modifyButtonWordCountSongs?.Refresh();
clearButtonWordCountSongs?.Refresh();
closeButtonWordCountSongs?.Refresh();
inputBoxWordCountSongs?.Refresh();
}
catch (Exception ex)
{
Console.WriteLine("Error in SetWordCountSongsAndButtonsVisibility: " + ex.Message);
}
};
// 檢查是否需要在主執行緒外執行
if (this.InvokeRequired)
{
this.Invoke(action); // 如果需要,透過主執行緒執行
}
else
{
action(); // 否則直接執行
}
}
private void NumberWordCountSongsButton_Click(object sender, EventArgs e)
{
var button = sender as Button;
if (button != null && button.Tag != null)
{
if (inputBoxWordCountSongs.Visible)
{
inputBoxWordCountSongs.Text += button.Tag.ToString();
}
}
}
}
}

View File

@ -0,0 +1,147 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace DualScreenDemo
{
/*
* (PrimaryForm)
* UI
*/
public partial class PrimaryForm
{
// 各種歌曲搜尋按鈕及其對應的背景圖片 (一般狀態 / 啟動狀態)
private Button zhuyinSearchSongButton;
private Bitmap zhuyinSearchSongNormalBackground;
private Bitmap zhuyinSearchSongActiveBackground;
private Button englishSearchSongButton;
private Bitmap englishSearchSongNormalBackground;
private Bitmap englishSearchSongActiveBackground;
private Button wordCountSearchSongButton;
private Bitmap wordCountSearchSongNormalBackground;
private Bitmap wordCountSearchSongActiveBackground;
private Button pinyinSearchSongButton;
private Bitmap pinyinSearchSongNormalBackground;
private Bitmap pinyinSearchSongActiveBackground;
private Button handWritingSearchSongButton;
private Bitmap handWritingSearchSongNormalBackground;
private Bitmap handWritingSearchSongActiveBackground;
private Button numberSearchSongButton;
private Bitmap numberSearchSongNormalBackground;
private Bitmap numberSearchSongActiveBackground;
/// <summary>
/// 點擊「歌曲搜尋」按鈕時的事件處理函式
/// 1. 更新導航按鈕的背景圖片,使「歌曲搜尋」按鈕顯示為啟動狀態
/// 2. 隱藏其他搜尋/分類 UI僅顯示歌曲搜尋選單
/// 3. 若有 QR Code 顯示,則將其隱藏
/// </summary>
private void SongSearchButton_Click(object sender, EventArgs e)
{
// 更新導航按鈕的背景圖片
newSongAlertButton.BackgroundImage = newSongAlertNormalBackground;
hotPlayButton.BackgroundImage = hotPlayNormalBackground;
singerSearchButton.BackgroundImage = singerSearchNormalBackground;
songSearchButton.BackgroundImage = songSearchActiveBackground;
languageSearchButton.BackgroundImage = languageSearchNormalBackground;
groupSearchButton.BackgroundImage = groupSearchNormalBackground;
categorySearchButton.BackgroundImage = categorySearchNormalBackground;
orderedSongsButton.BackgroundImage = orderedSongsNormalBackground;
myFavoritesButton.BackgroundImage = myFavoritesNormalBackground;
promotionsButton.BackgroundImage = promotionsNormalBackground;
deliciousFoodButton.BackgroundImage = deliciousFoodNormalBackground;
isOnOrderedSongsPage = false;
// 隱藏其他 UI
SetHotSongButtonsVisibility(false);
SetNewSongButtonsVisibility(false);
SetSingerSearchButtonsVisibility(false);
SetPictureBoxLanguageButtonsVisibility(false);
SetGroupButtonsVisibility(false);
SetPictureBoxCategoryAndButtonsVisibility(false);
SetZhuYinSingersAndButtonsVisibility(false);
SetZhuYinSongsAndButtonsVisibility(false);
SetEnglishSingersAndButtonsVisibility(false);
SetPinYinSingersAndButtonsVisibility(false);
SetPinYinSongsAndButtonsVisibility(false);
SetPictureBoxToggleLightAndButtonsVisibility(false);
SetPictureBoxSceneSoundEffectsAndButtonsVisibility(false);
// 顯示歌曲搜尋選單按鈕
SetSongSearchButtonsVisibility(true);
// 隱藏 QR Code (若有)
if (pictureBoxQRCode != null)
{
pictureBoxQRCode.Visible = false;
closeQRCodeButton.Visible = false;
}
}
/// <summary>
/// 設定「歌曲搜尋」相關按鈕的可見性
/// </summary>
/// <param name="isVisible">是否顯示</param>
private void SetSongSearchButtonsVisibility(bool isVisible)
{
pictureBox4.Visible = isVisible; // 控制搜尋 UI 背景的可見性
// 歌曲搜尋的按鈕陣列
Button[] songSearchButtons = {
zhuyinSearchSongButton,
englishSearchSongButton,
wordCountSearchSongButton,
pinyinSearchSongButton,
handWritingSearchSongButton,
numberSearchSongButton
};
// 設定按鈕可見性,若顯示則移至最前
foreach (var button in songSearchButtons)
{
button.Visible = isVisible;
if (isVisible)
{
button.BringToFront();
}
}
}
/// <summary>
/// 初始化所有「歌曲搜尋」按鈕
/// 依據不同的搜尋方式 (注音、英文、拼音、筆劃、手寫、數字) 建立對應按鈕
/// </summary>
private void InitializeButtonsForSongSearch()
{
// 初始化「注音搜尋」按鈕
InitializeSearchButton(ref zhuyinSearchSongButton, "zhuyinSearchSongButton",
1214, 230, 209, 59, ref zhuyinSearchSongNormalBackground, ref zhuyinSearchSongActiveBackground,
normalStateImageSongQuery, mouseDownImageSongQuery, ZhuyinSearchSongsButton_Click);
// 初始化「英文搜尋」按鈕
InitializeSearchButton(ref englishSearchSongButton, "englishSearchSongButton",
1214, 293, 209, 58, ref englishSearchSongNormalBackground, ref englishSearchSongActiveBackground,
normalStateImageSongQuery, mouseDownImageSongQuery, EnglishSearchSongsButton_Click);
// 初始化「拼音搜尋」按鈕
InitializeSearchButton(ref pinyinSearchSongButton, "pinyinSearchSongButton",
1214, 356, 209, 58, ref pinyinSearchSongNormalBackground, ref pinyinSearchSongActiveBackground,
normalStateImageSongQuery, mouseDownImageSongQuery, PinyinSearchSongsButton_Click);
// 初始化「筆劃搜尋」按鈕
InitializeSearchButton(ref wordCountSearchSongButton, "wordCountSearchSongButton",
1214, 418, 209, 59, ref wordCountSearchSongNormalBackground, ref wordCountSearchSongActiveBackground,
normalStateImageSongQuery, mouseDownImageSongQuery, WordCountSearchSongsButton_Click);
// 初始化「手寫搜尋」按鈕
InitializeSearchButton(ref handWritingSearchSongButton, "handWritingSearchSongButton",
1214, 481, 209, 59, ref handWritingSearchSongNormalBackground, ref handWritingSearchSongActiveBackground,
normalStateImageSongQuery, mouseDownImageSongQuery, HandWritingSearchButtonForSongs_Click);
// 初始化「數字搜尋」按鈕
InitializeSearchButton(ref numberSearchSongButton, "numberSearchSongButton",
1214, 544, 209, 58, ref numberSearchSongNormalBackground, ref numberSearchSongActiveBackground,
normalStateImageSongQuery, mouseDownImageSongQuery, SongIDSearchSongsButton_Click);
}
}
}

302
Program.cs Normal file
View File

@ -0,0 +1,302 @@
using System;
using System.Globalization;
using System.Windows.Forms;
using System.Data.SQLite;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.IO.Ports;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using OfficeOpenXml; // 引用 EPPlus 的命名空間
using DirectShowLib;
using DirectShowLib.Dvd;
using DirectShowLib.BDA;
using DirectShowLib.DES;
using DirectShowLib.DMO;
using ZXing;
using ZXing.QrCode;
using WMPLib;
using NAudio.Wave;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Microsoft.Ink;
using Microsoft.Win32;
using System.Diagnostics;
using DualScreenDemo;
using DBObj;
namespace DualScreenDemo
{
public static class Program
{
// 定义全局变量
internal static SongListManager songListManager;
internal static ArtistManager artistManager;
internal static SerialPortManager serialPortManager;
private static PrimaryForm primaryForm; // 儲存實例的參考
[STAThread]
static void Main()
{
try
{
// COM 初始化
int hr = ComInterop.CoInitializeEx(IntPtr.Zero, ComInterop.COINIT_APARTMENTTHREADED);
if (hr < 0)
{
Console.WriteLine("Failed to initialize COM library.");
return;
}
// URL ACL 配置
string ipAddress = "192.168.88.66";
string port = "9090";
string url = $"http://{ipAddress}:{port}/";
if (!IsUrlAclExists(url))
{
RunBatchFileToAddUrlAcl(ipAddress, port);
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 初始化管理器
songListManager = SongListManager.Instance; // 使用单例
artistManager = new ArtistManager();
var commandHandler = new CommandHandler(songListManager);
serialPortManager = new SerialPortManager(commandHandler);
serialPortManager.InitializeSerialPort();
// 輸出屏幕信息
Console.WriteLine($"Virtual Screen: {SystemInformation.VirtualScreen}");
foreach (var screen in Screen.AllScreens)
{
Console.WriteLine($"Screen: {screen.DeviceName} Resolution: {screen.Bounds.Width}x{screen.Bounds.Height}");
}
// 啟動服務器
Task.Run(() => HttpServerManager.StartServer());
Task.Run(() => TCPServerManager.StartServer());
// 註冊事件
Application.ApplicationExit += (sender, e) => SerialPortManager.CloseSerialPortSafely();
SystemEvents.DisplaySettingsChanged += OnDisplaySettingsChanged;
// 創建主窗體
primaryForm = new PrimaryForm();
primaryForm.allSongs = songListManager.AllSongs;
primaryForm.allArtists = artistManager.AllArtists;
primaryForm.StartPosition = FormStartPosition.Manual;
primaryForm.Location = new Point(0, 0);
primaryForm.Size = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
// 在完整初始化後檢查狀態
string stateFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "states.txt");
bool isClosedState = File.Exists(stateFilePath) &&
File.ReadAllText(stateFilePath).Trim().Equals("CLOSE", StringComparison.OrdinalIgnoreCase);
InitializeSecondaryScreen();
// 使用 Shown 事件來確保窗體完全加載後再顯示送客畫面
if (isClosedState)
{
primaryForm.Shown += (s, e) =>
{
primaryForm.ShowSendOffScreen();
};
}
primaryForm.Show();
Application.Run(primaryForm);
}
catch (Exception ex)
{
WriteLog(ex.ToString());
}
finally
{
SystemEvents.DisplaySettingsChanged -= OnDisplaySettingsChanged;
}
}
private static bool IsUrlAclExists(string url)
{
try
{
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "netsh",
Arguments = "http show urlacl",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (Process process = Process.Start(startInfo))
{
using (StreamReader reader = process.StandardOutput)
{
string output = reader.ReadToEnd();
return output.Contains(url); // 检查是否包含指定 URL
}
}
}
catch (Exception ex)
{
Console.WriteLine("检查 URL ACL 时出错: " + ex.Message);
return false;
}
}
// 动态创建并运行批处理文件以添加 URL ACL
private static void RunBatchFileToAddUrlAcl(string ipAddress, string port)
{
// 确保批处理文件在当前程序的同一目录下
string batchFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "AddUrlAcl.bat");
try
{
// 创建批处理内容
string batchContent =
$"netsh http add urlacl url=http://{ipAddress}:{port}/ user=Everyone\n";
// 写入批处理文件,确保使用 UTF-8 编码
File.WriteAllText(batchFilePath, batchContent);
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "cmd.exe", // 使用 cmd 执行
Arguments = $"/c \"{batchFilePath}\"", // /c 参数用于执行命令后关闭命令窗口
UseShellExecute = true, // 使用系统外壳程序来启动
Verb = "runas" // 以管理员身份运行
};
Process process = Process.Start(startInfo);
process.WaitForExit(); // 等待批处理执行完成
}
catch (Exception ex)
{
Console.WriteLine("执行批处理文件失败: " + ex.Message);
}
}
private static void InitializeSecondaryScreen()
{
if (Screen.AllScreens.Length > 1)
{
var secondaryScreen = Screen.AllScreens.FirstOrDefault(s => !s.Primary);
if (secondaryScreen != null)
{
// 确保 primaryForm 和 videoPlayerForm 已经正确初始化
if (primaryForm.videoPlayerForm == null)
{
primaryForm.videoPlayerForm = new VideoPlayerForm();
}
// 设置 videoPlayerForm 的位置和大小
// primaryForm.videoPlayerForm.StartPosition = FormStartPosition.Manual;
// primaryForm.videoPlayerForm.Location = secondaryScreen.WorkingArea.Location;
// primaryForm.videoPlayerForm.Size = secondaryScreen.WorkingArea.Size;
// 显示 videoPlayerForm 在第二显示器
primaryForm.videoPlayerForm.Show();
// 初始化公共播放列表
primaryForm.videoPlayerForm.InitializePublicPlaylist(primaryForm.publicSongList);
}
}
}
private static void OnDisplaySettingsChanged(object sender, EventArgs e)
{
// UI操作應該放在try-catch塊中
try
{
if (Screen.AllScreens.Length > 1)
{
primaryForm.Invoke(new System.Action(() =>
{
if (primaryForm.videoPlayerForm == null)
{
var filePath = @"C:\\video\\100015-周杰倫&aMei-不該-國語-vL-100-11000001.mpg";
if (File.Exists(filePath))
{
Screen secondaryScreen = Screen.AllScreens.FirstOrDefault(s => !s.Primary);
if (secondaryScreen != null)
{
primaryForm.videoPlayerForm = new VideoPlayerForm();
// primaryForm.primaryMediaPlayerForm = new PrimaryMediaPlayerForm(primaryForm, primaryForm.secondaryMediaPlayerForm);
primaryForm.videoPlayerForm.InitializePublicPlaylist(primaryForm.publicSongList);
primaryForm.videoPlayerForm.Show();
}
}
else
{
Console.WriteLine("File not found.");
}
}
}));
}
}
catch (Exception ex)
{
WriteLog("Error during display settings changed: " + ex.Message);
}
}
static void WriteLog(string message)
{
// 指定日志文件的路径
string logFilePath = "mainlog.txt"; // 请根据需要修改文件路径
try
{
// 使用 StreamWriter 来向日志文件追加文本
using (StreamWriter writer = new StreamWriter(logFilePath, true))
{
writer.WriteLine(String.Format("[{0}] {1}", DateTime.Now, message));
}
}
catch (Exception ex)
{
// 如果写入日志文件时发生错误,这里可以处理这些异常
// 例如:打印到控制台
Console.WriteLine(String.Format("Error writing to log file: {0}", ex.Message));
}
}
private static Form CreatePrimaryForm()
{
return new Form
{
WindowState = FormWindowState.Maximized,
FormBorderStyle = FormBorderStyle.None
};
}
private static Form CreateSecondaryForm(Screen screen)
{
return new Form
{
Text = "Secondary Screen Form",
StartPosition = FormStartPosition.Manual,
Bounds = screen.Bounds,
WindowState = FormWindowState.Maximized,
FormBorderStyle = FormBorderStyle.None
};
}
}
}

195
PromotionsAndMenuPanel.cs Normal file
View File

@ -0,0 +1,195 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace DualScreenDemo
{
public class PromotionsAndMenuPanel : Panel
{
public ImagePanel prevPagePanel;
public ImagePanel currentPagePanel;
public ImagePanel nextPagePanel;
private List<Image> promotionsAndMenuImages;
private const int ImagesPerPage = 1;
public int currentPageIndex = 0;
private Point initialMousePosition;
private bool isDragging = false;
private const int DragThreshold = 50;
private int maxPositiveDeltaX = 0;
private int maxNegativeDeltaX = 0;
private const int ShiftThreshold = 150;
public PromotionsAndMenuPanel()
{
this.DoubleBuffered = true;
this.AutoScroll = false;
InitializePages();
this.MouseDown += PromotionsAndMenuPanel_MouseDown;
this.MouseMove += PromotionsAndMenuPanel_MouseMove;
this.MouseUp += PromotionsAndMenuPanel_MouseUp;
}
private void InitializePages()
{
prevPagePanel = new ImagePanel();
currentPagePanel = new ImagePanel();
nextPagePanel = new ImagePanel();
PrimaryForm.ResizeAndPositionControl(prevPagePanel, -1440, 0, 1440, 900);
PrimaryForm.ResizeAndPositionControl(currentPagePanel, 0, 0, 1440, 900);
PrimaryForm.ResizeAndPositionControl(nextPagePanel, 1440, 0, 1440, 900);
this.Controls.Add(prevPagePanel);
this.Controls.Add(currentPagePanel);
this.Controls.Add(nextPagePanel);
}
public void LoadImages(List<Image> images)
{
promotionsAndMenuImages = images;
LoadPage(currentPageIndex - 1);
LoadPage(currentPageIndex);
LoadPage(currentPageIndex + 1);
}
private void LoadPage(int pageIndex)
{
ImagePanel targetPanel = IdentifyTargetPanel(pageIndex);
targetPanel.Controls.Clear();
if (pageIndex < 0 || pageIndex * ImagesPerPage >= promotionsAndMenuImages.Count)
{
return;
}
int start = pageIndex * ImagesPerPage;
int end = Math.Min(start + ImagesPerPage, promotionsAndMenuImages.Count);
for (int i = start; i < end; i++)
{
var image = promotionsAndMenuImages[i];
AddImagePanel(image, targetPanel);
}
}
private ImagePanel IdentifyTargetPanel(int pageIndex)
{
if (pageIndex == currentPageIndex - 1)
return prevPagePanel;
else if (pageIndex == currentPageIndex + 1)
return nextPagePanel;
else
return currentPagePanel;
}
private void AddImagePanel(Image image, Panel targetPanel)
{
PictureBox pictureBox = new PictureBox()
{
Image = image,
SizeMode = PictureBoxSizeMode.StretchImage,
Location = new Point(0, 0),
Size = new Size(targetPanel.Width, targetPanel.Height)
};
pictureBox.MouseDown += PromotionsAndMenuPanel_MouseDown;
pictureBox.MouseMove += PromotionsAndMenuPanel_MouseMove;
pictureBox.MouseUp += PromotionsAndMenuPanel_MouseUp;
targetPanel.Controls.Add(pictureBox);
}
private void PromotionsAndMenuPanel_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
initialMousePosition = e.Location;
isDragging = true;
this.Capture = true;
}
}
private void PromotionsAndMenuPanel_MouseMove(object sender, MouseEventArgs e)
{
if (isDragging)
{
int deltaX = e.X - initialMousePosition.X;
if (deltaX > maxPositiveDeltaX) maxPositiveDeltaX = deltaX;
if (deltaX < maxNegativeDeltaX) maxNegativeDeltaX = deltaX;
ShiftPages(deltaX);
}
}
private void PromotionsAndMenuPanel_MouseUp(object sender, MouseEventArgs e)
{
if (isDragging)
{
int deltaX = e.X - initialMousePosition.X;
isDragging = false;
this.Capture = false;
FinishShift(deltaX);
}
}
public void ShiftPages(int deltaX)
{
int newLeft = currentPagePanel.Left + deltaX;
if (newLeft > 1440) newLeft = 1440;
if (newLeft < -1440) newLeft = -1440;
prevPagePanel.Location = new Point(newLeft - 1440, 0);
currentPagePanel.Location = new Point(newLeft, 0);
nextPagePanel.Location = new Point(newLeft + 1440, 0);
this.Invalidate();
this.Update();
}
public void FinishShift(int deltaX)
{
if (maxPositiveDeltaX > ShiftThreshold)
{
LoadPreviousPage();
}
else if (maxNegativeDeltaX < -ShiftThreshold)
{
LoadNextPage();
}
prevPagePanel.Location = new Point(-1560, 0);
currentPagePanel.Location = new Point(0, 0);
nextPagePanel.Location = new Point(1440, 0);
maxPositiveDeltaX = 0;
maxNegativeDeltaX = 0;
}
public void LoadPreviousPage()
{
if (currentPageIndex > 0)
currentPageIndex--;
LoadPage(currentPageIndex - 1);
LoadPage(currentPageIndex);
LoadPage(currentPageIndex + 1);
}
public void LoadNextPage()
{
if (currentPageIndex < (promotionsAndMenuImages.Count - 1) / ImagesPerPage)
currentPageIndex++;
LoadPage(currentPageIndex - 1);
LoadPage(currentPageIndex);
LoadPage(currentPageIndex + 1);
}
}
}

12
RemoteCommand.cs Normal file
View File

@ -0,0 +1,12 @@
namespace DualScreenDemo
{
public enum RemoteCommand
{
NextPage,
PreviousPage,
Play,
Pause,
Stop
}
}

View File

@ -0,0 +1,9 @@
using System;
namespace DualScreenDemo
{
public class RemoteControlEventArgs : EventArgs
{
public RemoteCommand Command { get; set; }
}
}

8
RequestData.cs Normal file
View File

@ -0,0 +1,8 @@
namespace DualScreenDemo
{
public class RequestData
{
public string buttonId { get; set; }
public string text { get; set; }
}
}

27
SampleGrabberCallback.cs Normal file
View File

@ -0,0 +1,27 @@
using System;
using DirectShowLib;
namespace DualScreenDemo
{
public class SampleGrabberCallback : ISampleGrabberCB
{
private VideoPlayerForm form;
public SampleGrabberCallback(VideoPlayerForm form)
{
this.form = form;
}
public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
{
return 0;
}
public int SampleCB(double SampleTime, IMediaSample pSample)
{
return 0;
}
}
}

15
ScreenHelper.cs Normal file
View File

@ -0,0 +1,15 @@
using System;
using System.Linq;
using System.Windows.Forms;
namespace DualScreenDemo
{
public static class ScreenHelper
{
public static Screen GetSecondMonitor()
{
return Screen.AllScreens.FirstOrDefault(s => !s.Primary) ?? Screen.PrimaryScreen;
}
}
}

8
SearchRequest.cs Normal file
View File

@ -0,0 +1,8 @@
namespace DualScreenDemo
{
public class SearchRequest
{
public string Query { get; set; }
public string Type { get; set; }
}
}

63
SequenceManager.cs Normal file
View File

@ -0,0 +1,63 @@
namespace DualScreenDemo
{
public class SequenceManager
{
private ClickSequenceState currentState = ClickSequenceState.Initial;
public void ProcessClick(string position)
{
switch (currentState)
{
case ClickSequenceState.Initial:
if (position == "中間")
{
currentState = ClickSequenceState.FirstClicked;
}
break;
case ClickSequenceState.FirstClicked:
if (position == "右上")
{
currentState = ClickSequenceState.SecondClicked;
}
else
{
ResetState();
}
break;
case ClickSequenceState.SecondClicked:
if (position == "左上")
{
currentState = ClickSequenceState.ThirdClicked;
}
else
{
ResetState();
}
break;
case ClickSequenceState.ThirdClicked:
if (position == "謝謝")
{
currentState = ClickSequenceState.Completed;
PerformShutdown();
}
else
{
ResetState();
}
break;
}
}
private void ResetState()
{
currentState = ClickSequenceState.Initial;
}
private void PerformShutdown()
{
System.Diagnostics.Process.Start("shutdown", "/s /t 0");
}
}
}

200
SerialPortManager.cs Normal file
View File

@ -0,0 +1,200 @@
using System;
using System.IO.Ports;
using System.Text;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.IO;
using System.Diagnostics;
using System.Linq;
namespace DualScreenDemo
{
public class SerialPortManager
{
internal static SerialPort mySerialPort;
private readonly CommandHandler commandHandler;
public SerialPortManager(CommandHandler commandHandler)
{
this.commandHandler = commandHandler;
}
public void InitializeSerialPort()
{
string[] ports = SerialPort.GetPortNames();
Console.WriteLine("可用的串列埠:");
foreach (var port in ports)
{
Console.WriteLine(port);
}
// 定義優先選擇的串列埠順序
string[] preferredPorts = { "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM1" };
// 選擇第一個符合的串列埠
string selectedPort = preferredPorts.FirstOrDefault(port => ports.Contains(port));
if (!string.IsNullOrEmpty(selectedPort))
{
mySerialPort = new SerialPort(selectedPort);
Console.WriteLine($"已選擇串列埠: {selectedPort}");
}
else
{
MessageBox.Show("未找到任何可用的串列埠!");
return;
}
// 配置串列埠參數
mySerialPort.BaudRate = 9600;
mySerialPort.Parity = Parity.None;
mySerialPort.StopBits = StopBits.One;
mySerialPort.DataBits = 8;
mySerialPort.Handshake = Handshake.None;
// 綁定資料接收事件
mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
try
{
mySerialPort.Open();
Console.WriteLine($"{selectedPort} 串列埠已成功開啟。");
}
catch (Exception ex)
{
MessageBox.Show($"開啟 {selectedPort} 串列埠時發生錯誤: {ex.Message}");
}
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
try
{
SerialPort sp = (SerialPort)sender;
if (!sp.IsOpen)
{
// Console.WriteLine("串列埠未開啟,無法接收資料。");
return;
}
int bytesToRead = sp.BytesToRead;
if (bytesToRead > 0)
{
byte[] buffer = new byte[bytesToRead];
int bytesRead = sp.Read(buffer, 0, bytesToRead);
StringBuilder hexData = new StringBuilder(bytesRead * 2);
for (int i = 0; i < bytesRead; i++)
{
hexData.AppendFormat("{0:X2}", buffer[i]);
}
string indata = hexData.ToString();
// Console.WriteLine($"接收到的資料 (Hex): {indata}");
Task.Run(() =>
{
try
{
commandHandler.ProcessData(indata);
}
catch (Exception processEx)
{
Console.WriteLine($"處理資料時發生錯誤: {processEx.Message}");
}
});
}
else
{
// Console.WriteLine("未接收到任何資料。");
}
}
catch (Exception ex)
{
Console.WriteLine($"接收資料時發生錯誤: {ex.Message}");
}
}
public static void CloseSerialPortSafely()
{
if (mySerialPort != null)
{
try
{
if (mySerialPort.IsOpen)
{
mySerialPort.Close();
// Console.WriteLine("串列埠已成功關閉。");
}
}
catch (Exception ex)
{
Console.WriteLine($"關閉串列埠時發生錯誤: {ex.Message}");
}
}
}
// public void EnsurePortConnection()
// {
// try
// {
// if (mySerialPort == null || !mySerialPort.IsOpen)
// {
// // Console.WriteLine("串列埠已中斷,重新初始化...");
// InitializeSerialPort();
// }
// }
// catch (Exception ex)
// {
// // Console.WriteLine($"檢查串列埠連接時發生錯誤: {ex.Message}");
// }
// }
// public static void LogData(string data)
// {
// string filePath = Path.Combine(Application.StartupPath, "dataLog.txt");
// try
// {
// File.AppendAllText(filePath, $"{DateTime.Now}: {data}{Environment.NewLine}");
// // Console.WriteLine($"資料已記錄到日誌: {data}");
// }
// catch (Exception ex)
// {
// // Console.WriteLine($"記錄日誌時發生錯誤: {ex.Message}");
// }
// }
// public void CheckAndResetConnection()
// {
// if (mySerialPort == null || !mySerialPort.IsOpen)
// {
// // Console.WriteLine("發現串列埠問題,嘗試重啟連線...");
// EnsurePortConnection();
// }
// }
// public void SendHeartbeat()
// {
// if (mySerialPort != null && mySerialPort.IsOpen)
// {
// try
// {
// mySerialPort.Write("HEARTBEAT");
// // Console.WriteLine("已發送心跳信號");
// }
// catch (Exception ex)
// {
// // Console.WriteLine($"發送心跳信號時發生錯誤: {ex.Message}");
// }
// }
// }
}
}

302
TCPServer.cs Normal file
View File

@ -0,0 +1,302 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Drawing;
using System.Threading.Tasks;
using System.IO; // 為 Path 和 File 提供支持
using System.Windows.Forms; // 為 Invoke 和 Form 控件提供支持
using System.Collections.Generic;
using DBObj;
using OverlayFormObj;
namespace DualScreenDemo
{
public class TCPServer
{
private TcpListener listener;
private const int Port = 1000;
private readonly string hostNameSuffix;
//private bool isProcessingCommand = false;
public TCPServer()
{
listener = new TcpListener(IPAddress.Any, Port);
hostNameSuffix = GetHostNameSuffix();
}
private bool IsFormReady(Form form)
{
if (form == null) return false;
bool isReady = false;
try
{
if (form.IsHandleCreated && !form.IsDisposed)
{
if (form.InvokeRequired)
{
form.Invoke(new Action(() => isReady = true));
}
else
{
isReady = true;
}
}
}
catch
{
isReady = false;
}
return isReady;
}
private async Task SafeInvoke(Form form, Action action, int maxRetries = 10)
{
if (form == null) return;
for (int i = 0; i < maxRetries; i++)
{
try
{
if (IsFormReady(form))
{
if (form.InvokeRequired)
{
form.Invoke(action);
}
else
{
action();
}
return;
}
}
catch (Exception ex)
{
Console.WriteLine($"Invoke attempt {i + 1} failed: {ex.Message}");
}
await Task.Delay(500); // 等待500毫秒后重试
}
Console.WriteLine("Failed to invoke action after maximum retries");
}
public void Start()
{
listener.Start();
Console.WriteLine("Server started on port " + Port + ".");
try {
string stateFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "states.txt");
string initialState = ReadStateFile(stateFilePath);
if (initialState.Equals("CLOSE", StringComparison.OrdinalIgnoreCase))
{
_ = SafeInvoke(PrimaryForm.Instance, () =>
{
try {
foreach (Control ctrl in PrimaryForm.Instance.Controls)
{
ctrl.Enabled = false;
}
PrimaryForm.Instance.ShowSendOffScreen();
}
catch (Exception ex) {
Console.WriteLine($"顯示送客畫面時發生錯誤: {ex.Message}");
}
});
}
while (true)
{
Console.WriteLine("Waiting for connections...");
using (TcpClient client = listener.AcceptTcpClient())
{
Console.WriteLine("Connected!");
NetworkStream stream = client.GetStream();
while (client.Connected)
{
byte[] buffer = new byte[1024];
int bytesRead = stream.Read(buffer, 0, buffer.Length);
if (bytesRead == 0)
break;
string request = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine("Received: " + request.Trim());
if (request.Length < 5)
{
continue;
}
string requestHostSuffix = request.Substring(0, 3);
string command = request.Substring(4);
if (requestHostSuffix.Equals(hostNameSuffix, StringComparison.OrdinalIgnoreCase))
{
if (command.Trim().Equals("X", StringComparison.OrdinalIgnoreCase))
{
_ = SafeInvoke(VideoPlayerForm.Instance, async () =>
{
if (IsFormReady(PrimaryForm.Instance))
{
await SafeInvoke(PrimaryForm.Instance, () =>
{
PrimaryForm.Instance.ShowSendOffScreen();
Console.WriteLine("開始設置新的播放列表");
string closePath = @"C:\video\CLOSE.MPG";
if (File.Exists(closePath))
{
SongData closeSong = new SongData(
"", "", "結束播放", 0, "", "", "", "",
DateTime.Now, closePath, "", "", "", "",
"", "", "", "", "", "", "", 1
);
VideoPlayerForm.playingSongList = new List<SongData>();
if (VideoPlayerForm.Instance.currentPlayingSong != null)
{
VideoPlayerForm.playingSongList.Add(VideoPlayerForm.Instance.currentPlayingSong);
}
VideoPlayerForm.playingSongList.Add(closeSong);
PrimaryForm.userRequestedSongs = new List<SongData>();
if (IsFormReady(OverlayForm.MainForm))
{
OverlayForm.MainForm.nextSongLabel.Visible = false;
}
Console.WriteLine("已設置新的播放列表,包含當前歌曲和 CLOSE.MPG");
}
else
{
Console.WriteLine($"錯誤: 找不到檔案 {closePath}");
}
});
}
});
UpdateStateFile(stateFilePath, "CLOSE");
continue;
}
if (command.Trim().Equals("O", StringComparison.OrdinalIgnoreCase))
{
_ = SafeInvoke(PrimaryForm.Instance, () =>
{
PrimaryForm.Instance.HideSendOffScreen();
});
UpdateStateFile(stateFilePath, "OPEN");
continue;
}
}
if (IsFormReady(OverlayForm.MainForm))
{
string message = request.Trim();
string pattern = @"^(全部|\d{4})\((白色|紅色|綠色|黑色|藍色)\)-";
Match match = Regex.Match(message, pattern);
_ = SafeInvoke(OverlayForm.MainForm, () =>
{
if (match.Success)
{
string marqueeMessage = message.Substring(match.Value.Length).Trim();
Color textColor = GetColorFromString(match.Groups[2].Value);
OverlayForm.MainForm.UpdateMarqueeText(marqueeMessage, OverlayForm.MarqueeStartPosition.Middle, textColor);
}
else
{
string marqueeMessage = "系統公告: " + message;
OverlayForm.MainForm.UpdateMarqueeTextSecondLine(marqueeMessage);
}
});
}
if (request.Trim().Equals("exit", StringComparison.OrdinalIgnoreCase))
{
break;
}
}
Console.WriteLine("Connection closed.");
}
}
}
catch (Exception e)
{
Console.WriteLine($"Error: {e.Message}");
}
finally
{
listener.Stop();
}
}
private Color GetColorFromString(string colorName)
{
switch (colorName)
{
case "白色":
return Color.White;
case "紅色":
return Color.Red;
case "綠色":
return Color.LightGreen;
case "黑色":
return Color.Black;
case "藍色":
return Color.LightBlue;
default:
return Color.Black;
}
}
private string GetHostNameSuffix()
{
string hostName = Dns.GetHostName();
return hostName.Length > 3 ? hostName.Substring(hostName.Length - 3) : hostName;
}
private string ReadStateFile(string filePath)
{
try
{
if (File.Exists(filePath))
{
string state = File.ReadAllText(filePath).Trim();
Console.WriteLine($"✅ State file read: {filePath} -> {state}");
return state;
}
else
{
Console.WriteLine("⚠️ State file not found. Creating new file with default state: OPEN");
UpdateStateFile(filePath, "OPEN");
return "OPEN";
}
}
catch (Exception ex)
{
Console.WriteLine($"❌ Failed to read state file: {ex.Message}");
return "OPEN"; // 默認為 OPEN
}
}
private void UpdateStateFile(string filePath, string state)
{
try
{
File.WriteAllText(filePath, state);
Console.WriteLine($"✅ State file updated: {filePath} -> {state}");
}
catch (Exception ex)
{
Console.WriteLine($"❌ Failed to update state file: {ex.Message}");
}
}
}
}

11
TCPServerManager.cs Normal file
View File

@ -0,0 +1,11 @@
namespace DualScreenDemo
{
public static class TCPServerManager
{
public static void StartServer()
{
TCPServer server = new TCPServer();
server.Start();
}
}
}

1748
VideoPlayerForm.cs Normal file

File diff suppressed because it is too large Load Diff

8
WindowStyles.cs Normal file
View File

@ -0,0 +1,8 @@
namespace DualScreenDemo
{
public static class WindowStyles
{
public const int WS_CHILD = 0x40000000;
public const int WS_CLIPSIBLINGS = 0x04000000;
}
}

17
app.manifest Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v3">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<!-- 確保 manifestVersion 屬性存在 -->
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<manifestVersion>1.0</manifestVersion>
</windowsSettings>
</application>
</assembly>

Some files were not shown because too many files have changed in this diff Show More