1590 lines
60 KiB
C#
1590 lines
60 KiB
C#
using Ink_Canvas.Helpers;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Media;
|
||
using System.Timers;
|
||
using System.Windows;
|
||
using System.Windows.Controls;
|
||
using System.Windows.Input;
|
||
using System.Windows.Interop;
|
||
using System.Windows.Media;
|
||
using System.Windows.Threading;
|
||
using Newtonsoft.Json;
|
||
using System.Runtime.InteropServices;
|
||
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
|
||
|
||
namespace Ink_Canvas
|
||
{
|
||
/// <summary>
|
||
/// 最近点名记录数据模型
|
||
/// </summary>
|
||
public class RecentRollCallData
|
||
{
|
||
public List<string> RecentNames { get; set; } = new List<string>();
|
||
public DateTime LastUpdate { get; set; } = DateTime.Now;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 点名历史记录数据模型
|
||
/// </summary>
|
||
public class RollCallHistoryData
|
||
{
|
||
public List<string> History { get; set; } = new List<string>();
|
||
public Dictionary<string, int> NameFrequency { get; set; } = new Dictionary<string, int>();
|
||
public DateTime LastUpdate { get; set; } = DateTime.Now;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 新点名UI风格的窗口
|
||
/// </summary>
|
||
public partial class NewStyleRollCallWindow : Window
|
||
{
|
||
public NewStyleRollCallWindow()
|
||
{
|
||
InitializeComponent();
|
||
AnimationsHelper.ShowWithSlideFromBottomAndFade(this, 0.25);
|
||
|
||
InitializeUI();
|
||
ApplyTheme(MainWindow.Settings);
|
||
|
||
// 初始化点名相关变量
|
||
InitializeRollCallData();
|
||
}
|
||
|
||
public NewStyleRollCallWindow(bool isSingleDraw)
|
||
{
|
||
InitializeComponent();
|
||
AnimationsHelper.ShowWithSlideFromBottomAndFade(this, 0.25);
|
||
|
||
// 设置单次抽模式
|
||
isSingleDrawMode = isSingleDraw;
|
||
|
||
InitializeUI();
|
||
ApplyTheme(MainWindow.Settings);
|
||
|
||
// 初始化点名相关变量
|
||
InitializeRollCallData();
|
||
|
||
// 单次抽模式:自动开始抽选
|
||
if (isSingleDrawMode)
|
||
{
|
||
// 延迟100ms后自动开始抽选
|
||
new System.Threading.Thread(() =>
|
||
{
|
||
System.Threading.Thread.Sleep(100);
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
StartSingleDraw();
|
||
});
|
||
}).Start();
|
||
}
|
||
}
|
||
|
||
public NewStyleRollCallWindow(Settings settings, bool isSingleDraw = false)
|
||
{
|
||
InitializeComponent();
|
||
AnimationsHelper.ShowWithSlideFromBottomAndFade(this, 0.25);
|
||
|
||
// 保存设置
|
||
this.settings = settings;
|
||
|
||
// 设置单次抽模式
|
||
isSingleDrawMode = isSingleDraw;
|
||
|
||
InitializeUI();
|
||
ApplyTheme(settings);
|
||
|
||
// 初始化设置
|
||
InitializeSettings();
|
||
|
||
// 初始化点名相关变量
|
||
InitializeRollCallData();
|
||
|
||
// 单次抽模式:自动开始抽选
|
||
if (isSingleDrawMode)
|
||
{
|
||
// 延迟100ms后自动开始抽选
|
||
new System.Threading.Thread(() =>
|
||
{
|
||
System.Threading.Thread.Sleep(100);
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
StartSingleDraw();
|
||
});
|
||
}).Start();
|
||
}
|
||
}
|
||
|
||
#region 私有字段
|
||
private List<string> nameList = new List<string>();
|
||
private List<string> recentNames = new List<string>();
|
||
private int currentCount = 1;
|
||
private bool isRollCalling = false;
|
||
private Timer rollCallTimer;
|
||
private Random random = new Random();
|
||
private DateTime lastActivityTime = DateTime.Now;
|
||
|
||
// 机器学习相关
|
||
private RollCallHistoryData historyData = new RollCallHistoryData();
|
||
private int maxRecentHistory = 20;
|
||
private double avoidanceWeight = 0.8; // 避免重复的权重
|
||
private const double FREQUENCY_WEIGHT = 0.2; // 频率平衡的权重
|
||
|
||
// 单次抽相关
|
||
private bool isSingleDrawMode = false;
|
||
private Random singleDrawRandom = new Random();
|
||
|
||
// 设置相关
|
||
private Settings settings;
|
||
private int autoCloseWaitTime = 2500; // 自动关闭等待时间(毫秒)
|
||
|
||
// 点名模式
|
||
private string selectedRollCallMode = "Random"; // 默认随机点名
|
||
|
||
// JSON文件路径
|
||
private static readonly string ConfigsFolder = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs");
|
||
private static readonly string RollCallHistoryJsonPath = System.IO.Path.Combine(ConfigsFolder, "RollCallHistory.json");
|
||
#endregion
|
||
|
||
#region 初始化方法
|
||
private void InitializeUI()
|
||
{
|
||
UpdateCountDisplay();
|
||
LoadNamesFromFile();
|
||
UpdateListCountDisplay();
|
||
LoadRollCallHistory();
|
||
LoadSettings();
|
||
InitializeSingleDrawMode();
|
||
InitializeModeSelection();
|
||
InitializeExternalCallerSelection();
|
||
}
|
||
|
||
private void InitializeSingleDrawMode()
|
||
{
|
||
if (isSingleDrawMode)
|
||
{
|
||
// 单次抽模式:使用60个数字
|
||
MainResultDisplay.Text = "准备抽选...";
|
||
StatusDisplay.Text = "单次抽模式 - 60个数字";
|
||
CountDisplay.Text = "1";
|
||
CountMinusBtn.IsEnabled = true;
|
||
CountPlusBtn.IsEnabled = true;
|
||
}
|
||
else
|
||
{
|
||
// 普通点名模式
|
||
MainResultDisplay.Text = "点击开始点名";
|
||
StatusDisplay.Text = "准备就绪";
|
||
CountDisplay.Text = "1";
|
||
CountMinusBtn.IsEnabled = true;
|
||
CountPlusBtn.IsEnabled = true;
|
||
}
|
||
}
|
||
|
||
private void InitializeModeSelection()
|
||
{
|
||
try
|
||
{
|
||
SetModeSelection("Random");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.WriteLogToFile($"初始化点名模式选择控件时出错: {ex.Message}", LogHelper.LogType.Error);
|
||
}
|
||
}
|
||
|
||
private void InitializeExternalCallerSelection()
|
||
{
|
||
try
|
||
{
|
||
SetExternalCallerSelection("ClassIsland");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.WriteLogToFile($"初始化外部点名选择控件时出错: {ex.Message}", LogHelper.LogType.Error);
|
||
}
|
||
}
|
||
|
||
private void InitializeSettings()
|
||
{
|
||
try
|
||
{
|
||
if (settings?.RandSettings != null)
|
||
{
|
||
// 使用老点名UI的设置
|
||
autoCloseWaitTime = (int)settings.RandSettings.RandWindowOnceCloseLatency * 1000;
|
||
maxRecentHistory = settings.RandSettings.MLAvoidanceHistoryCount;
|
||
avoidanceWeight = settings.RandSettings.MLAvoidanceWeight;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.WriteLogToFile($"初始化点名设置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||
}
|
||
}
|
||
|
||
private void LoadSettings()
|
||
{
|
||
try
|
||
{
|
||
if (MainWindow.Settings?.RandSettings != null)
|
||
{
|
||
maxRecentHistory = MainWindow.Settings.RandSettings.MLAvoidanceHistoryCount;
|
||
avoidanceWeight = MainWindow.Settings.RandSettings.MLAvoidanceWeight;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.WriteLogToFile($"加载点名设置时出错: {ex.Message}", LogHelper.LogType.Error);
|
||
}
|
||
}
|
||
|
||
private void InitializeRollCallData()
|
||
{
|
||
// 初始化点名定时器
|
||
rollCallTimer = new Timer(100);
|
||
rollCallTimer.Elapsed += RollCallTimer_Elapsed;
|
||
}
|
||
|
||
private void ApplyTheme()
|
||
{
|
||
try
|
||
{
|
||
// 应用主题设置
|
||
if (MainWindow.Settings != null)
|
||
{
|
||
ApplyTheme(MainWindow.Settings);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.WriteLogToFile($"应用新点名UI窗口主题出错: {ex.Message}", LogHelper.LogType.Error);
|
||
}
|
||
}
|
||
|
||
private void ApplyTheme(Settings settings)
|
||
{
|
||
try
|
||
{
|
||
if (settings.Appearance.Theme == 0) // 浅色主题
|
||
{
|
||
iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Light);
|
||
ApplyThemeResources("Light");
|
||
}
|
||
else if (settings.Appearance.Theme == 1) // 深色主题
|
||
{
|
||
iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Dark);
|
||
ApplyThemeResources("Dark");
|
||
SetDarkThemeBorder();
|
||
}
|
||
else // 跟随系统主题
|
||
{
|
||
bool isSystemLight = IsSystemThemeLight();
|
||
if (isSystemLight)
|
||
{
|
||
iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Light);
|
||
ApplyThemeResources("Light");
|
||
}
|
||
else
|
||
{
|
||
iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Dark);
|
||
ApplyThemeResources("Dark");
|
||
SetDarkThemeBorder();
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.WriteLogToFile($"应用新点名UI窗口主题出错: {ex.Message}", LogHelper.LogType.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 应用主题资源
|
||
/// </summary>
|
||
/// <param name="theme">主题类型(Light或Dark)</param>
|
||
private void ApplyThemeResources(string theme)
|
||
{
|
||
try
|
||
{
|
||
// 更新窗口资源
|
||
var resources = this.Resources;
|
||
|
||
if (theme == "Light")
|
||
{
|
||
// 应用浅色主题资源
|
||
resources["NewRollCallWindowBackground"] = new SolidColorBrush(Color.FromRgb(255, 255, 255));
|
||
resources["NewRollCallWindowBorderBrush"] = new SolidColorBrush(Color.FromRgb(228, 228, 231));
|
||
resources["NewRollCallWindowTitleForeground"] = new SolidColorBrush(Color.FromRgb(24, 24, 27));
|
||
resources["NewRollCallWindowDigitForeground"] = new SolidColorBrush(Color.FromRgb(24, 24, 27));
|
||
resources["NewRollCallWindowButtonBackground"] = new SolidColorBrush(Color.FromRgb(244, 244, 245));
|
||
resources["NewRollCallWindowButtonForeground"] = new SolidColorBrush(Color.FromRgb(24, 24, 27));
|
||
resources["NewRollCallWindowPrimaryButtonBackground"] = new SolidColorBrush(Color.FromRgb(76, 175, 80)); // #4CAF50
|
||
resources["NewRollCallWindowPrimaryButtonForeground"] = new SolidColorBrush(Color.FromRgb(255, 255, 255));
|
||
resources["NewRollCallWindowSecondaryTextForeground"] = new SolidColorBrush(Color.FromRgb(113, 113, 122));
|
||
}
|
||
else
|
||
{
|
||
// 应用深色主题资源 - 与新计时器窗口统一
|
||
resources["NewRollCallWindowBackground"] = new SolidColorBrush(Color.FromRgb(31, 31, 31)); // #1f1f1f
|
||
resources["NewRollCallWindowBorderBrush"] = new SolidColorBrush(Color.FromRgb(224, 224, 224)); // #E0E0E0
|
||
resources["NewRollCallWindowTitleForeground"] = new SolidColorBrush(Colors.White);
|
||
resources["NewRollCallWindowDigitForeground"] = new SolidColorBrush(Colors.White);
|
||
resources["NewRollCallWindowButtonBackground"] = new SolidColorBrush(Color.FromRgb(42, 42, 42)); // #2a2a2a
|
||
resources["NewRollCallWindowButtonForeground"] = new SolidColorBrush(Colors.White);
|
||
resources["NewRollCallWindowPrimaryButtonBackground"] = new SolidColorBrush(Color.FromRgb(76, 175, 80)); // #4CAF50
|
||
resources["NewRollCallWindowPrimaryButtonForeground"] = new SolidColorBrush(Colors.White);
|
||
resources["NewRollCallWindowSecondaryTextForeground"] = new SolidColorBrush(Color.FromRgb(156, 163, 175)); // #9ca3af
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.WriteLogToFile($"应用新点名UI窗口主题资源出错: {ex.Message}", LogHelper.LogType.Error);
|
||
}
|
||
}
|
||
|
||
private bool IsSystemThemeLight()
|
||
{
|
||
var light = false;
|
||
try
|
||
{
|
||
var registryKey = Microsoft.Win32.Registry.CurrentUser;
|
||
var themeKey = registryKey.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize");
|
||
if (themeKey != null)
|
||
{
|
||
var value = themeKey.GetValue("AppsUseLightTheme");
|
||
if (value != null)
|
||
{
|
||
light = (int)value == 1;
|
||
}
|
||
themeKey.Close();
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
light = true;
|
||
}
|
||
return light;
|
||
}
|
||
#endregion
|
||
|
||
#region 点名模式逻辑
|
||
/// <summary>
|
||
/// 根据选择的模式进行点名
|
||
/// </summary>
|
||
private List<string> SelectNamesByMode(List<string> availableNames, int count)
|
||
{
|
||
switch (selectedRollCallMode)
|
||
{
|
||
case "Random":
|
||
return SelectNamesWithML(availableNames, count);
|
||
case "Sequential":
|
||
return SelectNamesSequentially(availableNames, count);
|
||
case "Group":
|
||
return SelectNamesInGroups(availableNames, count);
|
||
default:
|
||
return SelectNamesWithML(availableNames, count);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 顺序点名:按名单顺序选择
|
||
/// </summary>
|
||
private List<string> SelectNamesSequentially(List<string> availableNames, int count)
|
||
{
|
||
if (availableNames.Count == 0) return new List<string>();
|
||
|
||
var selectedNames = new List<string>();
|
||
int startIndex = 0;
|
||
|
||
// 从历史记录中找到上次选择的位置
|
||
if (historyData.History != null && historyData.History.Count > 0)
|
||
{
|
||
string lastSelected = historyData.History.LastOrDefault();
|
||
if (!string.IsNullOrEmpty(lastSelected))
|
||
{
|
||
int lastIndex = availableNames.IndexOf(lastSelected);
|
||
if (lastIndex >= 0)
|
||
{
|
||
startIndex = (lastIndex + 1) % availableNames.Count;
|
||
}
|
||
}
|
||
}
|
||
|
||
for (int i = 0; i < count && i < availableNames.Count; i++)
|
||
{
|
||
int index = (startIndex + i) % availableNames.Count;
|
||
selectedNames.Add(availableNames[index]);
|
||
}
|
||
|
||
return selectedNames;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分组点名:将名单分成若干组,每组选择一个人
|
||
/// </summary>
|
||
private List<string> SelectNamesInGroups(List<string> availableNames, int count)
|
||
{
|
||
if (availableNames.Count == 0) return new List<string>();
|
||
|
||
var selectedNames = new List<string>();
|
||
int groupSize = Math.Max(1, availableNames.Count / count);
|
||
|
||
for (int i = 0; i < count && i * groupSize < availableNames.Count; i++)
|
||
{
|
||
int startIndex = i * groupSize;
|
||
int endIndex = Math.Min(startIndex + groupSize, availableNames.Count);
|
||
|
||
// 从当前组中随机选择一个人
|
||
var group = availableNames.GetRange(startIndex, endIndex - startIndex);
|
||
if (group.Count > 0)
|
||
{
|
||
int randomIndex = random.Next(0, group.Count);
|
||
selectedNames.Add(group[randomIndex]);
|
||
}
|
||
}
|
||
|
||
return selectedNames;
|
||
}
|
||
#endregion
|
||
|
||
#region 机器学习避免重复逻辑
|
||
/// <summary>
|
||
/// 使用机器学习算法选择点名人员,避免最近重复
|
||
/// </summary>
|
||
/// <param name="availableNames">可用名单</param>
|
||
/// <param name="count">需要选择的人数</param>
|
||
/// <returns>选择的人员名单</returns>
|
||
private List<string> SelectNamesWithML(List<string> availableNames, int count)
|
||
{
|
||
if (availableNames == null || availableNames.Count == 0)
|
||
return new List<string>();
|
||
|
||
// 检查是否启用机器学习避免重复
|
||
bool enableML = MainWindow.Settings?.RandSettings?.EnableMLAvoidance ?? true;
|
||
if (!enableML)
|
||
{
|
||
// 如果禁用机器学习,使用简单随机选择
|
||
return SelectNamesRandomly(availableNames, count);
|
||
}
|
||
|
||
var selectedNames = new List<string>();
|
||
var remainingNames = new List<string>(availableNames);
|
||
|
||
for (int i = 0; i < count && remainingNames.Count > 0; i++)
|
||
{
|
||
string selectedName = SelectSingleNameWithML(remainingNames, selectedNames);
|
||
if (!string.IsNullOrEmpty(selectedName))
|
||
{
|
||
selectedNames.Add(selectedName);
|
||
remainingNames.Remove(selectedName);
|
||
}
|
||
}
|
||
|
||
return selectedNames;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 简单随机选择点名人员
|
||
/// </summary>
|
||
private List<string> SelectNamesRandomly(List<string> availableNames, int count)
|
||
{
|
||
if (availableNames == null || availableNames.Count == 0)
|
||
return new List<string>();
|
||
|
||
var selectedNames = new List<string>();
|
||
var remainingNames = new List<string>(availableNames);
|
||
|
||
for (int i = 0; i < count && remainingNames.Count > 0; i++)
|
||
{
|
||
int randomIndex = random.Next(remainingNames.Count);
|
||
selectedNames.Add(remainingNames[randomIndex]);
|
||
remainingNames.RemoveAt(randomIndex);
|
||
}
|
||
|
||
return selectedNames;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 使用机器学习算法选择单个人员
|
||
/// </summary>
|
||
private string SelectSingleNameWithML(List<string> availableNames, List<string> alreadySelected)
|
||
{
|
||
if (availableNames.Count == 0) return null;
|
||
if (availableNames.Count == 1) return availableNames[0];
|
||
|
||
// 计算每个人员的权重
|
||
var nameWeights = new Dictionary<string, double>();
|
||
|
||
foreach (string name in availableNames)
|
||
{
|
||
if (alreadySelected.Contains(name)) continue;
|
||
|
||
double weight = 1.0; // 基础权重
|
||
|
||
// 1. 避免最近重复的权重计算
|
||
double recentAvoidanceWeight = CalculateRecentAvoidanceWeight(name);
|
||
weight *= (1.0 - recentAvoidanceWeight * avoidanceWeight);
|
||
|
||
// 2. 频率平衡权重计算
|
||
double frequencyWeight = CalculateFrequencyWeight(name);
|
||
weight *= (1.0 + frequencyWeight * FREQUENCY_WEIGHT);
|
||
|
||
// 3. 确保权重不为负数
|
||
weight = Math.Max(weight, 0.1);
|
||
|
||
nameWeights[name] = weight;
|
||
}
|
||
|
||
// 使用加权随机选择
|
||
return WeightedRandomSelection(nameWeights);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算避免最近重复的权重
|
||
/// </summary>
|
||
private double CalculateRecentAvoidanceWeight(string name)
|
||
{
|
||
if (historyData.History == null || historyData.History.Count == 0)
|
||
return 0.0;
|
||
|
||
// 获取最近记录
|
||
var recentHistory = historyData.History.Skip(Math.Max(0, historyData.History.Count - maxRecentHistory)).ToList();
|
||
int recentCount = recentHistory.Count(n => n == name);
|
||
|
||
// 计算权重:最近出现次数越多,权重越高(越应该避免)
|
||
return (double)recentCount / Math.Min(recentHistory.Count, maxRecentHistory);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算频率平衡权重
|
||
/// </summary>
|
||
private double CalculateFrequencyWeight(string name)
|
||
{
|
||
if (historyData.NameFrequency == null || !historyData.NameFrequency.ContainsKey(name))
|
||
return 0.5; // 如果从未被选中,给予中等权重
|
||
|
||
int totalSelections = historyData.NameFrequency.Values.Sum();
|
||
if (totalSelections == 0) return 0.5;
|
||
|
||
int nameCount = historyData.NameFrequency[name];
|
||
double frequency = (double)nameCount / totalSelections;
|
||
|
||
// 频率越低,权重越高(越应该被选中)
|
||
return 1.0 - frequency;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加权随机选择
|
||
/// </summary>
|
||
private string WeightedRandomSelection(Dictionary<string, double> nameWeights)
|
||
{
|
||
if (nameWeights.Count == 0) return null;
|
||
|
||
double totalWeight = nameWeights.Values.Sum();
|
||
if (totalWeight <= 0) return nameWeights.Keys.First();
|
||
|
||
double randomValue = random.NextDouble() * totalWeight;
|
||
double currentWeight = 0;
|
||
|
||
foreach (var kvp in nameWeights)
|
||
{
|
||
currentWeight += kvp.Value;
|
||
if (randomValue <= currentWeight)
|
||
{
|
||
return kvp.Key;
|
||
}
|
||
}
|
||
|
||
return nameWeights.Keys.Last();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新点名历史记录
|
||
/// </summary>
|
||
private void UpdateRollCallHistory(List<string> selectedNames)
|
||
{
|
||
if (selectedNames == null || selectedNames.Count == 0) return;
|
||
|
||
// 更新历史记录
|
||
if (historyData.History == null)
|
||
historyData.History = new List<string>();
|
||
|
||
historyData.History.AddRange(selectedNames);
|
||
|
||
// 保持历史记录不超过100条
|
||
if (historyData.History.Count > 100)
|
||
{
|
||
historyData.History = historyData.History.Skip(historyData.History.Count - 100).ToList();
|
||
}
|
||
|
||
// 更新频率统计
|
||
if (historyData.NameFrequency == null)
|
||
historyData.NameFrequency = new Dictionary<string, int>();
|
||
|
||
foreach (string name in selectedNames)
|
||
{
|
||
if (historyData.NameFrequency.ContainsKey(name))
|
||
historyData.NameFrequency[name]++;
|
||
else
|
||
historyData.NameFrequency[name] = 1;
|
||
}
|
||
|
||
historyData.LastUpdate = DateTime.Now;
|
||
|
||
// 保存到文件
|
||
SaveRollCallHistory();
|
||
}
|
||
#endregion
|
||
|
||
#region 数据持久化
|
||
/// <summary>
|
||
/// 加载点名历史记录
|
||
/// </summary>
|
||
private void LoadRollCallHistory()
|
||
{
|
||
try
|
||
{
|
||
if (!Directory.Exists(ConfigsFolder))
|
||
{
|
||
Directory.CreateDirectory(ConfigsFolder);
|
||
}
|
||
|
||
if (!File.Exists(RollCallHistoryJsonPath))
|
||
{
|
||
historyData = new RollCallHistoryData();
|
||
return;
|
||
}
|
||
|
||
string jsonContent = File.ReadAllText(RollCallHistoryJsonPath);
|
||
var data = JsonConvert.DeserializeObject<RollCallHistoryData>(jsonContent);
|
||
|
||
if (data != null)
|
||
{
|
||
historyData = data;
|
||
}
|
||
else
|
||
{
|
||
historyData = new RollCallHistoryData();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.WriteLogToFile($"加载点名历史记录失败: {ex.Message}", LogHelper.LogType.Error);
|
||
historyData = new RollCallHistoryData();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存点名历史记录
|
||
/// </summary>
|
||
private void SaveRollCallHistory()
|
||
{
|
||
try
|
||
{
|
||
if (!Directory.Exists(ConfigsFolder))
|
||
{
|
||
Directory.CreateDirectory(ConfigsFolder);
|
||
}
|
||
|
||
string jsonContent = JsonConvert.SerializeObject(historyData, Formatting.Indented);
|
||
File.WriteAllText(RollCallHistoryJsonPath, jsonContent);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.WriteLogToFile($"保存点名历史记录失败: {ex.Message}", LogHelper.LogType.Error);
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region UI更新方法
|
||
private void UpdateCountDisplay()
|
||
{
|
||
CountDisplay.Text = currentCount.ToString();
|
||
}
|
||
|
||
private void UpdateListCountDisplay()
|
||
{
|
||
ListCountDisplay.Text = $"名单人数: {nameList.Count}";
|
||
}
|
||
|
||
private void UpdateStatusDisplay(string status)
|
||
{
|
||
StatusDisplay.Text = status;
|
||
}
|
||
|
||
private void ShowResults(List<string> results)
|
||
{
|
||
if (results == null || results.Count == 0)
|
||
{
|
||
MainResultDisplay.Text = "无结果";
|
||
MultiResultScrollViewer.Visibility = Visibility.Collapsed;
|
||
return;
|
||
}
|
||
|
||
if (results.Count == 1)
|
||
{
|
||
MainResultDisplay.Text = results[0];
|
||
MainResultDisplay.Visibility = Visibility.Visible;
|
||
MultiResultScrollViewer.Visibility = Visibility.Collapsed;
|
||
}
|
||
else
|
||
{
|
||
// 多个结果时,隐藏主显示区域,显示多结果区域
|
||
MainResultDisplay.Text = "";
|
||
MainResultDisplay.Visibility = Visibility.Collapsed;
|
||
MultiResultScrollViewer.Visibility = Visibility.Visible;
|
||
|
||
// 显示所有结果(最多20个)
|
||
Result1Display.Text = results.Count > 0 ? results[0] : "";
|
||
Result2Display.Text = results.Count > 1 ? results[1] : "";
|
||
Result3Display.Text = results.Count > 2 ? results[2] : "";
|
||
Result4Display.Text = results.Count > 3 ? results[3] : "";
|
||
Result5Display.Text = results.Count > 4 ? results[4] : "";
|
||
Result6Display.Text = results.Count > 5 ? results[5] : "";
|
||
Result7Display.Text = results.Count > 6 ? results[6] : "";
|
||
Result8Display.Text = results.Count > 7 ? results[7] : "";
|
||
Result9Display.Text = results.Count > 8 ? results[8] : "";
|
||
Result10Display.Text = results.Count > 9 ? results[9] : "";
|
||
Result11Display.Text = results.Count > 10 ? results[10] : "";
|
||
Result12Display.Text = results.Count > 11 ? results[11] : "";
|
||
Result13Display.Text = results.Count > 12 ? results[12] : "";
|
||
Result14Display.Text = results.Count > 13 ? results[13] : "";
|
||
Result15Display.Text = results.Count > 14 ? results[14] : "";
|
||
Result16Display.Text = results.Count > 15 ? results[15] : "";
|
||
Result17Display.Text = results.Count > 16 ? results[16] : "";
|
||
Result18Display.Text = results.Count > 17 ? results[17] : "";
|
||
Result19Display.Text = results.Count > 18 ? results[18] : "";
|
||
Result20Display.Text = results.Count > 19 ? results[19] : "";
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 事件处理方法
|
||
private void CountPlus_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (isRollCalling) return;
|
||
|
||
// 获取老点名UI的设置
|
||
int maxPeopleLimit = settings?.RandSettings?.RandWindowOnceMaxStudents ?? 10;
|
||
|
||
if (isSingleDrawMode)
|
||
{
|
||
// 单次抽模式:最多选择60个数字,但受设置限制
|
||
int maxCount = Math.Min(maxPeopleLimit, 60);
|
||
currentCount = Math.Min(currentCount + 1, maxCount);
|
||
}
|
||
else
|
||
{
|
||
// 普通点名模式:根据是否有名单决定上限
|
||
int maxCount;
|
||
if (nameList.Count == 0)
|
||
{
|
||
// 没有名单时,使用60个数字
|
||
maxCount = Math.Min(maxPeopleLimit, 60);
|
||
}
|
||
else
|
||
{
|
||
// 有名单时,使用名单人数
|
||
maxCount = Math.Min(maxPeopleLimit, nameList.Count);
|
||
}
|
||
currentCount = Math.Min(currentCount + 1, maxCount);
|
||
}
|
||
|
||
UpdateCountDisplay();
|
||
}
|
||
|
||
private void CountMinus_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (isRollCalling) return;
|
||
currentCount = Math.Max(currentCount - 1, 1);
|
||
UpdateCountDisplay();
|
||
}
|
||
|
||
private void ImportList_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 打开名单导入窗口,与老点名UI保持一致
|
||
var namesInputWindow = new NamesInputWindow();
|
||
namesInputWindow.ShowDialog();
|
||
|
||
// 重新加载名单
|
||
LoadNamesFromFile();
|
||
UpdateListCountDisplay();
|
||
UpdateStatusDisplay($"已导入 {nameList.Count} 个名字");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show($"导入名单失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||
LogHelper.WriteLogToFile($"导入名单失败: {ex.Message}", LogHelper.LogType.Error);
|
||
}
|
||
}
|
||
|
||
private void LoadNamesFromFile()
|
||
{
|
||
try
|
||
{
|
||
string namesFilePath = App.RootPath + "Names.txt";
|
||
if (File.Exists(namesFilePath))
|
||
{
|
||
string content = File.ReadAllText(namesFilePath);
|
||
nameList = content.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
|
||
.Select(name => name.Trim())
|
||
.Where(name => !string.IsNullOrEmpty(name))
|
||
.ToList();
|
||
}
|
||
else
|
||
{
|
||
nameList.Clear();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.WriteLogToFile($"加载名单文件失败: {ex.Message}", LogHelper.LogType.Error);
|
||
nameList.Clear();
|
||
}
|
||
}
|
||
|
||
private void ClearList_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
nameList.Clear();
|
||
UpdateListCountDisplay();
|
||
UpdateStatusDisplay("名单已清空");
|
||
}
|
||
|
||
private void SetModeSelection(string mode)
|
||
{
|
||
try
|
||
{
|
||
// 存储选择的模式
|
||
selectedRollCallMode = mode;
|
||
|
||
// 重置所有按钮状态
|
||
RandomModeText.FontWeight = FontWeights.Normal;
|
||
RandomModeText.Opacity = 0.6;
|
||
RandomModeText.Foreground = new SolidColorBrush(Color.FromRgb(102, 102, 102));
|
||
SequentialModeText.FontWeight = FontWeights.Normal;
|
||
SequentialModeText.Opacity = 0.6;
|
||
SequentialModeText.Foreground = new SolidColorBrush(Color.FromRgb(102, 102, 102));
|
||
GroupModeText.FontWeight = FontWeights.Normal;
|
||
GroupModeText.Opacity = 0.6;
|
||
GroupModeText.Foreground = new SolidColorBrush(Color.FromRgb(102, 102, 102));
|
||
|
||
// 设置选中状态和动画
|
||
switch (mode)
|
||
{
|
||
case "Random":
|
||
RandomModeText.FontWeight = FontWeights.Bold;
|
||
RandomModeText.Opacity = 1.0;
|
||
RandomModeText.Foreground = new SolidColorBrush(Colors.White);
|
||
SegmentedIndicator.HorizontalAlignment = HorizontalAlignment.Left;
|
||
SegmentedIndicator.CornerRadius = new CornerRadius(7.5, 0, 0, 7.5);
|
||
|
||
// 添加动画效果
|
||
var randomAnimation = new System.Windows.Media.Animation.ThicknessAnimation(
|
||
new Thickness(0, 0, 0, 0),
|
||
TimeSpan.FromMilliseconds(200));
|
||
SegmentedIndicator.BeginAnimation(Border.MarginProperty, randomAnimation);
|
||
|
||
UpdateStatusDisplay("已选择点名模式: 随机点名");
|
||
break;
|
||
case "Sequential":
|
||
SequentialModeText.FontWeight = FontWeights.Bold;
|
||
SequentialModeText.Opacity = 1.0;
|
||
SequentialModeText.Foreground = new SolidColorBrush(Colors.White);
|
||
SegmentedIndicator.HorizontalAlignment = HorizontalAlignment.Left;
|
||
SegmentedIndicator.CornerRadius = new CornerRadius(0, 0, 0, 0);
|
||
|
||
// 添加动画效果 - 移动到中间位置
|
||
var sequentialAnimation = new System.Windows.Media.Animation.ThicknessAnimation(
|
||
new Thickness(100, 0, 0, 0),
|
||
TimeSpan.FromMilliseconds(200));
|
||
SegmentedIndicator.BeginAnimation(Border.MarginProperty, sequentialAnimation);
|
||
|
||
UpdateStatusDisplay("已选择点名模式: 顺序点名");
|
||
break;
|
||
case "Group":
|
||
GroupModeText.FontWeight = FontWeights.Bold;
|
||
GroupModeText.Opacity = 1.0;
|
||
GroupModeText.Foreground = new SolidColorBrush(Colors.White);
|
||
SegmentedIndicator.HorizontalAlignment = HorizontalAlignment.Left;
|
||
SegmentedIndicator.CornerRadius = new CornerRadius(0, 7.5, 7.5, 0);
|
||
|
||
// 添加动画效果 - 移动到右侧位置
|
||
var groupAnimation = new System.Windows.Media.Animation.ThicknessAnimation(
|
||
new Thickness(200, 0, 0, 0),
|
||
TimeSpan.FromMilliseconds(200));
|
||
SegmentedIndicator.BeginAnimation(Border.MarginProperty, groupAnimation);
|
||
|
||
UpdateStatusDisplay("已选择点名模式: 分组点名");
|
||
break;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.WriteLogToFile($"设置点名模式选择时出错: {ex.Message}", LogHelper.LogType.Error);
|
||
}
|
||
}
|
||
|
||
private void RandomMode_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
SetModeSelection("Random");
|
||
}
|
||
|
||
private void SequentialMode_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
SetModeSelection("Sequential");
|
||
}
|
||
|
||
private void GroupMode_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
SetModeSelection("Group");
|
||
}
|
||
|
||
private static bool isExternalCallerFirstClick = true;
|
||
private string selectedExternalCaller = "ClassIsland";
|
||
|
||
private void ExternalCaller_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (isExternalCallerFirstClick)
|
||
{
|
||
MessageBox.Show(
|
||
"首次使用外部点名功能,请确保已安装相应的点名软件。\n" +
|
||
"如未安装,请前往官网下载并安装后再使用。如果已安装请再次点击此按钮。",
|
||
"提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
isExternalCallerFirstClick = false;
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
string protocol = "";
|
||
switch (selectedExternalCaller)
|
||
{
|
||
case "ClassIsland":
|
||
protocol = "classisland://plugins/IslandCaller/Simple/1";
|
||
break;
|
||
case "SecRandom":
|
||
protocol = "secrandom://direct_extraction";
|
||
break;
|
||
case "NamePicker":
|
||
protocol = "namepicker://";
|
||
break;
|
||
default:
|
||
protocol = "classisland://plugins/IslandCaller/Simple/1";
|
||
break;
|
||
}
|
||
|
||
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
|
||
{
|
||
FileName = protocol,
|
||
UseShellExecute = true
|
||
});
|
||
|
||
UpdateStatusDisplay($"已启动外部点名: {selectedExternalCaller}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show("无法调用外部点名:" + ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||
LogHelper.WriteLogToFile($"外部点名调用失败: {ex.Message}", LogHelper.LogType.Error);
|
||
}
|
||
}
|
||
|
||
private void ClassIsland_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
SetExternalCallerSelection("ClassIsland");
|
||
}
|
||
|
||
private void SecRandom_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
SetExternalCallerSelection("SecRandom");
|
||
}
|
||
|
||
private void NamePicker_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
SetExternalCallerSelection("NamePicker");
|
||
}
|
||
|
||
private void SetExternalCallerSelection(string caller)
|
||
{
|
||
try
|
||
{
|
||
// 重置所有按钮状态
|
||
ClassIslandText.FontWeight = FontWeights.Normal;
|
||
ClassIslandText.Opacity = 0.6;
|
||
ClassIslandText.Foreground = new SolidColorBrush(Color.FromRgb(102, 102, 102));
|
||
SecRandomText.FontWeight = FontWeights.Normal;
|
||
SecRandomText.Opacity = 0.6;
|
||
SecRandomText.Foreground = new SolidColorBrush(Color.FromRgb(102, 102, 102));
|
||
NamePickerText.FontWeight = FontWeights.Normal;
|
||
NamePickerText.Opacity = 0.6;
|
||
NamePickerText.Foreground = new SolidColorBrush(Color.FromRgb(102, 102, 102));
|
||
|
||
// 设置选中状态和动画
|
||
switch (caller)
|
||
{
|
||
case "ClassIsland":
|
||
ClassIslandText.FontWeight = FontWeights.Bold;
|
||
ClassIslandText.Opacity = 1.0;
|
||
ClassIslandText.Foreground = new SolidColorBrush(Colors.White);
|
||
ExternalCallerIndicator.HorizontalAlignment = HorizontalAlignment.Left;
|
||
ExternalCallerIndicator.CornerRadius = new CornerRadius(7.5, 0, 0, 7.5);
|
||
|
||
// 添加动画效果
|
||
var classIslandAnimation = new System.Windows.Media.Animation.ThicknessAnimation(
|
||
new Thickness(0, 0, 0, 0),
|
||
TimeSpan.FromMilliseconds(200));
|
||
ExternalCallerIndicator.BeginAnimation(Border.MarginProperty, classIslandAnimation);
|
||
|
||
selectedExternalCaller = "ClassIsland";
|
||
UpdateStatusDisplay("已选择外部点名: ClassIsland");
|
||
break;
|
||
case "SecRandom":
|
||
SecRandomText.FontWeight = FontWeights.Bold;
|
||
SecRandomText.Opacity = 1.0;
|
||
SecRandomText.Foreground = new SolidColorBrush(Colors.White);
|
||
ExternalCallerIndicator.HorizontalAlignment = HorizontalAlignment.Left;
|
||
ExternalCallerIndicator.CornerRadius = new CornerRadius(0, 0, 0, 0);
|
||
|
||
// 添加动画效果
|
||
var secRandomAnimation = new System.Windows.Media.Animation.ThicknessAnimation(
|
||
new Thickness(120, 0, 0, 0),
|
||
TimeSpan.FromMilliseconds(200));
|
||
ExternalCallerIndicator.BeginAnimation(Border.MarginProperty, secRandomAnimation);
|
||
|
||
selectedExternalCaller = "SecRandom";
|
||
UpdateStatusDisplay("已选择外部点名: SecRandom");
|
||
break;
|
||
case "NamePicker":
|
||
NamePickerText.FontWeight = FontWeights.Bold;
|
||
NamePickerText.Opacity = 1.0;
|
||
NamePickerText.Foreground = new SolidColorBrush(Colors.White);
|
||
ExternalCallerIndicator.HorizontalAlignment = HorizontalAlignment.Left;
|
||
ExternalCallerIndicator.CornerRadius = new CornerRadius(0, 7.5, 7.5, 0);
|
||
|
||
// 添加动画效果
|
||
var namePickerAnimation = new System.Windows.Media.Animation.ThicknessAnimation(
|
||
new Thickness(240, 0, 0, 0),
|
||
TimeSpan.FromMilliseconds(200));
|
||
ExternalCallerIndicator.BeginAnimation(Border.MarginProperty, namePickerAnimation);
|
||
|
||
selectedExternalCaller = "NamePicker";
|
||
UpdateStatusDisplay("已选择外部点名: NamePicker");
|
||
break;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.WriteLogToFile($"设置外部点名选择时出错: {ex.Message}", LogHelper.LogType.Error);
|
||
}
|
||
}
|
||
|
||
private void StartRollCall_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (isSingleDrawMode)
|
||
{
|
||
// 单次抽模式:直接开始抽选
|
||
StartSingleDraw();
|
||
}
|
||
else
|
||
{
|
||
// 普通点名模式:检查名单或使用60个数字
|
||
if (nameList.Count == 0)
|
||
{
|
||
// 没有导入名单时,使用60个数字
|
||
StartRollCallWithNumbers();
|
||
}
|
||
else
|
||
{
|
||
// 有名单时,使用名单
|
||
if (currentCount > nameList.Count)
|
||
{
|
||
MessageBox.Show($"点名人数不能超过名单人数!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||
return;
|
||
}
|
||
StartRollCall();
|
||
}
|
||
}
|
||
}
|
||
|
||
private void StopRollCall_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
StopRollCall();
|
||
}
|
||
|
||
private void Reset_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (isRollCalling)
|
||
{
|
||
StopRollCall();
|
||
}
|
||
|
||
MainResultDisplay.Text = "点击开始点名";
|
||
MainResultDisplay.Visibility = Visibility.Visible;
|
||
MultiResultScrollViewer.Visibility = Visibility.Collapsed;
|
||
UpdateStatusDisplay("准备就绪");
|
||
}
|
||
|
||
private void StartRollCall()
|
||
{
|
||
isRollCalling = true;
|
||
StartRollCallBtn.Visibility = Visibility.Collapsed;
|
||
StopRollCallBtn.Visibility = Visibility.Visible;
|
||
UpdateStatusDisplay("正在点名...");
|
||
|
||
// 启动点名动画
|
||
StartRollCallAnimation();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 点名动画
|
||
/// </summary>
|
||
private void StartRollCallAnimation()
|
||
{
|
||
const int animationTimes = 100; // 动画次数
|
||
const int sleepTime = 5; // 每次动画间隔(毫秒)
|
||
|
||
new System.Threading.Thread(() =>
|
||
{
|
||
List<string> usedNames = new List<string>();
|
||
|
||
for (int i = 0; i < animationTimes; i++)
|
||
{
|
||
// 随机选择一个名字进行动画显示
|
||
if (nameList.Count > 0)
|
||
{
|
||
int randomIndex = new Random().Next(0, nameList.Count);
|
||
string displayName = nameList[randomIndex];
|
||
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
MainResultDisplay.Text = displayName;
|
||
});
|
||
}
|
||
|
||
System.Threading.Thread.Sleep(sleepTime);
|
||
}
|
||
|
||
// 动画结束,显示最终结果
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
// 根据选择的模式进行不同的点名逻辑
|
||
var selectedNames = SelectNamesByMode(nameList, currentCount);
|
||
|
||
// 更新历史记录
|
||
UpdateRollCallHistory(selectedNames);
|
||
|
||
// 显示结果
|
||
ShowResults(selectedNames);
|
||
UpdateStatusDisplay($"点名完成,共选择 {selectedNames.Count} 人");
|
||
|
||
// 停止点名状态
|
||
isRollCalling = false;
|
||
StartRollCallBtn.Visibility = Visibility.Visible;
|
||
StopRollCallBtn.Visibility = Visibility.Collapsed;
|
||
});
|
||
}).Start();
|
||
}
|
||
|
||
private void StartRollCallWithNumbers()
|
||
{
|
||
isRollCalling = true;
|
||
StartRollCallBtn.Visibility = Visibility.Collapsed;
|
||
StopRollCallBtn.Visibility = Visibility.Visible;
|
||
UpdateStatusDisplay("正在抽选...");
|
||
|
||
// 启动数字抽选动画
|
||
StartNumberRollCallAnimation();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 数字抽选动画
|
||
/// </summary>
|
||
private void StartNumberRollCallAnimation()
|
||
{
|
||
const int animationTimes = 100; // 动画次数
|
||
const int sleepTime = 5; // 每次动画间隔(毫秒)
|
||
|
||
new System.Threading.Thread(() =>
|
||
{
|
||
List<int> usedNumbers = new List<int>();
|
||
|
||
for (int i = 0; i < animationTimes; i++)
|
||
{
|
||
// 随机选择一个数字进行动画显示
|
||
int randomNumber = new Random().Next(1, 61); // 1-60
|
||
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
MainResultDisplay.Text = randomNumber.ToString();
|
||
});
|
||
|
||
System.Threading.Thread.Sleep(sleepTime);
|
||
}
|
||
|
||
// 动画结束,显示最终结果
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
// 使用60个数字进行抽选
|
||
var selectedNumbers = SelectMultipleNumbers(currentCount);
|
||
|
||
// 更新历史记录
|
||
UpdateRollCallHistory(selectedNumbers);
|
||
|
||
// 显示结果
|
||
ShowResults(selectedNumbers);
|
||
UpdateStatusDisplay($"抽选完成,共选择 {selectedNumbers.Count} 个数字");
|
||
|
||
// 停止点名状态
|
||
isRollCalling = false;
|
||
StartRollCallBtn.Visibility = Visibility.Visible;
|
||
StopRollCallBtn.Visibility = Visibility.Collapsed;
|
||
});
|
||
}).Start();
|
||
}
|
||
|
||
private void StopRollCall()
|
||
{
|
||
isRollCalling = false;
|
||
StartRollCallBtn.Visibility = Visibility.Visible;
|
||
StopRollCallBtn.Visibility = Visibility.Collapsed;
|
||
UpdateStatusDisplay("已停止点名");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 开始单次抽选
|
||
/// </summary>
|
||
private void StartSingleDraw()
|
||
{
|
||
isRollCalling = true;
|
||
StartRollCallBtn.Visibility = Visibility.Collapsed;
|
||
StopRollCallBtn.Visibility = Visibility.Visible;
|
||
UpdateStatusDisplay("正在抽选...");
|
||
|
||
// 启动抽选动画
|
||
StartSingleDrawAnimation();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 单次抽选动画
|
||
/// </summary>
|
||
private void StartSingleDrawAnimation()
|
||
{
|
||
const int animationTimes = 100; // 动画次数
|
||
const int sleepTime = 5; // 每次动画间隔(毫秒),参考老点名窗口
|
||
|
||
new System.Threading.Thread(() =>
|
||
{
|
||
if (nameList.Count > 0)
|
||
{
|
||
// 有名单时,从名单中抽选
|
||
StartSingleDrawNameAnimation(animationTimes, sleepTime);
|
||
}
|
||
else
|
||
{
|
||
// 没有名单时,从1-60数字中抽选
|
||
StartSingleDrawNumberAnimation(animationTimes, sleepTime);
|
||
}
|
||
}).Start();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 单次抽选名单动画
|
||
/// </summary>
|
||
private void StartSingleDrawNameAnimation(int animationTimes, int sleepTime)
|
||
{
|
||
List<string> usedNames = new List<string>();
|
||
|
||
for (int i = 0; i < animationTimes; i++)
|
||
{
|
||
// 随机选择一个名字进行动画显示,避免立即重复
|
||
string randomName;
|
||
do
|
||
{
|
||
randomName = nameList[singleDrawRandom.Next(0, nameList.Count)];
|
||
} while (usedNames.Count > 0 && usedNames[usedNames.Count - 1] == randomName);
|
||
|
||
usedNames.Add(randomName);
|
||
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
MainResultDisplay.Text = randomName;
|
||
});
|
||
|
||
System.Threading.Thread.Sleep(sleepTime);
|
||
}
|
||
|
||
// 动画结束,显示最终结果
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
// 根据选择的模式进行不同的点名逻辑
|
||
var selectedNames = SelectNamesByMode(nameList, currentCount);
|
||
|
||
// 更新历史记录
|
||
UpdateRollCallHistory(selectedNames);
|
||
|
||
// 显示结果
|
||
ShowResults(selectedNames);
|
||
UpdateStatusDisplay($"抽选完成,共选择 {selectedNames.Count} 人");
|
||
|
||
// 停止点名状态
|
||
isRollCalling = false;
|
||
StartRollCallBtn.Visibility = Visibility.Visible;
|
||
StopRollCallBtn.Visibility = Visibility.Collapsed;
|
||
|
||
if (isSingleDrawMode)
|
||
{
|
||
new System.Threading.Thread(() =>
|
||
{
|
||
System.Threading.Thread.Sleep(autoCloseWaitTime);
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
Close();
|
||
});
|
||
}).Start();
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 单次抽选数字动画
|
||
/// </summary>
|
||
private void StartSingleDrawNumberAnimation(int animationTimes, int sleepTime)
|
||
{
|
||
List<int> usedNumbers = new List<int>();
|
||
|
||
for (int i = 0; i < animationTimes; i++)
|
||
{
|
||
// 随机选择一个数字进行动画显示,避免立即重复
|
||
int randomNumber;
|
||
do
|
||
{
|
||
randomNumber = singleDrawRandom.Next(1, 61); // 1-60
|
||
} while (usedNumbers.Count > 0 && usedNumbers[usedNumbers.Count - 1] == randomNumber);
|
||
|
||
usedNumbers.Add(randomNumber);
|
||
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
MainResultDisplay.Text = randomNumber.ToString();
|
||
});
|
||
|
||
System.Threading.Thread.Sleep(sleepTime);
|
||
}
|
||
|
||
// 动画结束,显示最终结果
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
// 根据选择的数量进行抽选
|
||
var selectedNumbers = SelectMultipleNumbers(currentCount);
|
||
|
||
if (selectedNumbers.Count == 1)
|
||
{
|
||
MainResultDisplay.Text = selectedNumbers[0];
|
||
UpdateStatusDisplay($"抽选完成:{selectedNumbers[0]}");
|
||
}
|
||
else
|
||
{
|
||
MainResultDisplay.Text = "抽选结果";
|
||
MultiResultPanel.Visibility = Visibility.Visible;
|
||
|
||
Result1Display.Text = selectedNumbers.Count > 0 ? selectedNumbers[0] : "";
|
||
Result2Display.Text = selectedNumbers.Count > 1 ? selectedNumbers[1] : "";
|
||
Result3Display.Text = selectedNumbers.Count > 2 ? selectedNumbers[2] : "";
|
||
|
||
UpdateStatusDisplay($"抽选完成,共选择 {selectedNumbers.Count} 个数字");
|
||
}
|
||
|
||
// 停止点名状态
|
||
isRollCalling = false;
|
||
StartRollCallBtn.Visibility = Visibility.Visible;
|
||
StopRollCallBtn.Visibility = Visibility.Collapsed;
|
||
|
||
if (isSingleDrawMode)
|
||
{
|
||
new System.Threading.Thread(() =>
|
||
{
|
||
System.Threading.Thread.Sleep(autoCloseWaitTime);
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
Close();
|
||
});
|
||
}).Start();
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选择多个数字(不重复)
|
||
/// </summary>
|
||
private List<string> SelectMultipleNumbers(int count)
|
||
{
|
||
var selectedNumbers = new List<string>();
|
||
var usedNumbers = new List<int>();
|
||
|
||
for (int i = 0; i < count && usedNumbers.Count < 60; i++)
|
||
{
|
||
int randomNumber = singleDrawRandom.Next(1, 61); // 1-60
|
||
|
||
// 避免重复选择
|
||
while (usedNumbers.Contains(randomNumber))
|
||
{
|
||
randomNumber = singleDrawRandom.Next(1, 61);
|
||
}
|
||
|
||
usedNumbers.Add(randomNumber);
|
||
selectedNumbers.Add(randomNumber.ToString());
|
||
}
|
||
|
||
return selectedNumbers;
|
||
}
|
||
|
||
private void RollCallTimer_Elapsed(object sender, ElapsedEventArgs e)
|
||
{
|
||
// 这里可以实现点名动画效果
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
// 动画逻辑可以在这里实现
|
||
});
|
||
}
|
||
#endregion
|
||
|
||
#region 窗口事件处理
|
||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||
{
|
||
// 窗口加载时的初始化
|
||
}
|
||
|
||
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||
{
|
||
rollCallTimer?.Stop();
|
||
}
|
||
|
||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
Close();
|
||
}
|
||
|
||
private void WindowDragMove(object sender, MouseEventArgs e)
|
||
{
|
||
if (e.LeftButton == MouseButtonState.Pressed)
|
||
DragMove();
|
||
}
|
||
|
||
private void Window_MouseMove(object sender, MouseEventArgs e)
|
||
{
|
||
lastActivityTime = DateTime.Now;
|
||
}
|
||
|
||
private void Window_MouseEnter(object sender, MouseEventArgs e)
|
||
{
|
||
lastActivityTime = DateTime.Now;
|
||
}
|
||
|
||
private void SetDarkThemeBorder()
|
||
{
|
||
try
|
||
{
|
||
if (MainBorder != null)
|
||
{
|
||
MainBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(64, 64, 64));
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// 忽略错误
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region Win32 API 声明和置顶管理
|
||
[DllImport("user32.dll")]
|
||
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
|
||
|
||
[DllImport("user32.dll")]
|
||
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
|
||
|
||
[DllImport("user32.dll")]
|
||
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
|
||
|
||
[DllImport("user32.dll")]
|
||
private static extern bool IsWindow(IntPtr hWnd);
|
||
|
||
[DllImport("user32.dll")]
|
||
private static extern bool IsWindowVisible(IntPtr hWnd);
|
||
|
||
[DllImport("user32.dll")]
|
||
private static extern bool IsIconic(IntPtr hWnd);
|
||
|
||
[DllImport("user32.dll")]
|
||
private static extern IntPtr GetForegroundWindow();
|
||
|
||
[DllImport("user32.dll")]
|
||
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
||
|
||
[DllImport("kernel32.dll")]
|
||
private static extern uint GetCurrentProcessId();
|
||
|
||
private const int GWL_EXSTYLE = -20;
|
||
private const int WS_EX_TOPMOST = 0x00000008;
|
||
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
|
||
private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
|
||
private const uint SWP_NOMOVE = 0x0002;
|
||
private const uint SWP_NOSIZE = 0x0001;
|
||
private const uint SWP_NOACTIVATE = 0x0010;
|
||
private const uint SWP_SHOWWINDOW = 0x0040;
|
||
private const uint SWP_NOOWNERZORDER = 0x0200;
|
||
|
||
/// <summary>
|
||
/// 应用点名窗口置顶
|
||
/// </summary>
|
||
private void ApplyRollCallWindowTopmost()
|
||
{
|
||
try
|
||
{
|
||
var hwnd = new WindowInteropHelper(this).Handle;
|
||
if (hwnd == IntPtr.Zero) return;
|
||
|
||
// 强制激活窗口
|
||
Activate();
|
||
Focus();
|
||
|
||
// 设置WPF的Topmost属性
|
||
Topmost = true;
|
||
|
||
// 使用Win32 API强制置顶
|
||
// 1. 设置窗口样式为置顶
|
||
int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
||
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
|
||
|
||
// 2. 使用SetWindowPos确保窗口在最顶层
|
||
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
|
||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
|
||
|
||
LogHelper.WriteLogToFile("点名窗口已应用置顶", LogHelper.LogType.Trace);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogHelper.WriteLogToFile($"应用点名窗口置顶失败: {ex.Message}", LogHelper.LogType.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 窗口加载事件处理,确保置顶
|
||
/// </summary>
|
||
private void RollCallWindow_Loaded(object sender, RoutedEventArgs e)
|
||
{
|
||
// 使用延迟确保窗口完全加载后再应用置顶
|
||
Dispatcher.BeginInvoke(new Action(() =>
|
||
{
|
||
ApplyRollCallWindowTopmost();
|
||
}), DispatcherPriority.Loaded);
|
||
}
|
||
#endregion
|
||
}
|
||
}
|
||
|