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
{
///
/// 最近点名记录数据模型
///
public class RecentRollCallData
{
public List RecentNames { get; set; } = new List();
public DateTime LastUpdate { get; set; } = DateTime.Now;
}
///
/// 点名历史记录数据模型
///
public class RollCallHistoryData
{
public List History { get; set; } = new List();
public Dictionary NameFrequency { get; set; } = new Dictionary();
public DateTime LastUpdate { get; set; } = DateTime.Now;
}
///
/// 新点名UI风格的窗口
///
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 nameList = new List();
private List recentNames = new List();
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);
}
}
///
/// 应用主题资源
///
/// 主题类型(Light或Dark)
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 点名模式逻辑
///
/// 根据选择的模式进行点名
///
private List SelectNamesByMode(List 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);
}
}
///
/// 顺序点名:按名单顺序选择
///
private List SelectNamesSequentially(List availableNames, int count)
{
if (availableNames.Count == 0) return new List();
var selectedNames = new List();
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;
}
///
/// 分组点名:将名单分成若干组,每组选择一个人
///
private List SelectNamesInGroups(List availableNames, int count)
{
if (availableNames.Count == 0) return new List();
var selectedNames = new List();
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 机器学习避免重复逻辑
///
/// 使用机器学习算法选择点名人员,避免最近重复
///
/// 可用名单
/// 需要选择的人数
/// 选择的人员名单
private List SelectNamesWithML(List availableNames, int count)
{
if (availableNames == null || availableNames.Count == 0)
return new List();
// 检查是否启用机器学习避免重复
bool enableML = MainWindow.Settings?.RandSettings?.EnableMLAvoidance ?? true;
if (!enableML)
{
// 如果禁用机器学习,使用简单随机选择
return SelectNamesRandomly(availableNames, count);
}
var selectedNames = new List();
var remainingNames = new List(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;
}
///
/// 简单随机选择点名人员
///
private List SelectNamesRandomly(List availableNames, int count)
{
if (availableNames == null || availableNames.Count == 0)
return new List();
var selectedNames = new List();
var remainingNames = new List(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;
}
///
/// 使用机器学习算法选择单个人员
///
private string SelectSingleNameWithML(List availableNames, List alreadySelected)
{
if (availableNames.Count == 0) return null;
if (availableNames.Count == 1) return availableNames[0];
// 计算每个人员的权重
var nameWeights = new Dictionary();
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);
}
///
/// 计算避免最近重复的权重
///
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);
}
///
/// 计算频率平衡权重
///
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;
}
///
/// 加权随机选择
///
private string WeightedRandomSelection(Dictionary 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();
}
///
/// 更新点名历史记录
///
private void UpdateRollCallHistory(List selectedNames)
{
if (selectedNames == null || selectedNames.Count == 0) return;
// 更新历史记录
if (historyData.History == null)
historyData.History = new List();
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();
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 数据持久化
///
/// 加载点名历史记录
///
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(jsonContent);
if (data != null)
{
historyData = data;
}
else
{
historyData = new RollCallHistoryData();
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"加载点名历史记录失败: {ex.Message}", LogHelper.LogType.Error);
historyData = new RollCallHistoryData();
}
}
///
/// 保存点名历史记录
///
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 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();
}
///
/// 点名动画
///
private void StartRollCallAnimation()
{
const int animationTimes = 100; // 动画次数
const int sleepTime = 5; // 每次动画间隔(毫秒)
new System.Threading.Thread(() =>
{
List usedNames = new List();
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();
}
///
/// 数字抽选动画
///
private void StartNumberRollCallAnimation()
{
const int animationTimes = 100; // 动画次数
const int sleepTime = 5; // 每次动画间隔(毫秒)
new System.Threading.Thread(() =>
{
List usedNumbers = new List();
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("已停止点名");
}
///
/// 开始单次抽选
///
private void StartSingleDraw()
{
isRollCalling = true;
StartRollCallBtn.Visibility = Visibility.Collapsed;
StopRollCallBtn.Visibility = Visibility.Visible;
UpdateStatusDisplay("正在抽选...");
// 启动抽选动画
StartSingleDrawAnimation();
}
///
/// 单次抽选动画
///
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();
}
///
/// 单次抽选名单动画
///
private void StartSingleDrawNameAnimation(int animationTimes, int sleepTime)
{
List usedNames = new List();
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();
}
});
}
///
/// 单次抽选数字动画
///
private void StartSingleDrawNumberAnimation(int animationTimes, int sleepTime)
{
List usedNumbers = new List();
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();
}
});
}
///
/// 选择多个数字(不重复)
///
private List SelectMultipleNumbers(int count)
{
var selectedNumbers = new List();
var usedNumbers = new List();
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;
///
/// 应用点名窗口置顶
///
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);
}
}
///
/// 窗口加载事件处理,确保置顶
///
private void RollCallWindow_Loaded(object sender, RoutedEventArgs e)
{
// 使用延迟确保窗口完全加载后再应用置顶
Dispatcher.BeginInvoke(new Action(() =>
{
ApplyRollCallWindowTopmost();
}), DispatcherPriority.Loaded);
}
#endregion
}
}