feat(更新窗体): 优化更新流程和窗体显示

- 添加进度条更新逻辑,确保进度显示准确
- 设置窗体位置到屏幕右下角并置顶显示
- 移除窗体边框,优化视觉效果
- 添加总文件数统计,完善下载进度显示
- 优化状态更新逻辑,确保UI实时刷新
This commit is contained in:
Dong 2025-05-07 13:58:32 +08:00
parent 2a339142ad
commit 52ce8400ba
2 changed files with 173 additions and 20 deletions

2
Form1.Designer.cs generated
View File

@ -67,10 +67,12 @@
this.Controls.Add(this.Status_Box); this.Controls.Add(this.Status_Box);
this.Controls.Add(this.Update_Pro); this.Controls.Add(this.Update_Pro);
this.Controls.Add(this.Update_Text); this.Controls.Add(this.Update_Text);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.MaximizeBox = false; this.MaximizeBox = false;
this.MinimizeBox = false; this.MinimizeBox = false;
this.Name = "Update"; this.Name = "Update";
this.Text = "自动更新"; this.Text = "自动更新";
this.TopMost = true;
this.Load += new System.EventHandler(this.Update_Load); this.Load += new System.EventHandler(this.Update_Load);
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout(); this.PerformLayout();

183
Form1.cs
View File

@ -38,6 +38,8 @@ namespace CheckDownload
private Dictionary<string, (byte[] Data, string ExpectedMd5)> _downloadedFiles = new Dictionary<string, (byte[], string)>(); private Dictionary<string, (byte[] Data, string ExpectedMd5)> _downloadedFiles = new Dictionary<string, (byte[], string)>();
/// <summary>已完成下载的文件数</summary> /// <summary>已完成下载的文件数</summary>
private int _completedFiles = 0; private int _completedFiles = 0;
/// <summary>需要下载的总文件数</summary>
private int _totalFilesToDownload = 0;
/// <summary> /// <summary>
/// 构造函数,初始化更新窗体 /// 构造函数,初始化更新窗体
@ -59,6 +61,49 @@ namespace CheckDownload
Update_Pro.Step = 1; // 设置步进值 Update_Pro.Step = 1; // 设置步进值
} }
/// <summary>
/// 更新进度条值
/// </summary>
/// <param name="percentage">进度百分比(0-100)</param>
private void UpdateProgressValue(int percentage)
{
// 确保百分比在有效范围内
if (percentage < 0) percentage = 0;
if (percentage > 100) percentage = 100;
// 在UI线程上更新进度条值
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
// 确保进度条值不超出范围
if (percentage >= Update_Pro.Minimum && percentage <= Update_Pro.Maximum)
{
Update_Pro.Value = percentage;
}
else
{
// 如果超出范围,则设置为最大或最小值
Update_Pro.Value = Math.Min(Math.Max(percentage, Update_Pro.Minimum), Update_Pro.Maximum);
}
// 添加Application.DoEvents()调用,确保消息队列被处理
Application.DoEvents();
});
}
else
{
if (percentage >= Update_Pro.Minimum && percentage <= Update_Pro.Maximum)
{
Update_Pro.Value = percentage;
}
else
{
Update_Pro.Value = Math.Min(Math.Max(percentage, Update_Pro.Minimum), Update_Pro.Maximum);
}
Application.DoEvents();
}
}
/// <summary> /// <summary>
/// 窗体加载事件处理方法,启动更新流程 /// 窗体加载事件处理方法,启动更新流程
/// </summary> /// </summary>
@ -66,8 +111,27 @@ namespace CheckDownload
/// <param name="e">事件参数</param> /// <param name="e">事件参数</param>
public async void Update_Load(object sender, EventArgs e) public async void Update_Load(object sender, EventArgs e)
{ {
// 设置窗体位置到屏幕右下角
PositionFormToBottomRight();
await UpdateFile(); // 执行更新流程 await UpdateFile(); // 执行更新流程
this.Close(); // 更新完成后关闭窗体 // 注意窗体关闭逻辑已移至UpdateFile方法中确保显示完成消息后再关闭
}
/// <summary>
/// 设置窗体位置到屏幕右下角
/// </summary>
private void PositionFormToBottomRight()
{
// 获取当前屏幕的工作区域(排除任务栏等系统元素)
Rectangle workingArea = Screen.GetWorkingArea(this);
// 计算窗体在右下角的位置
int x = workingArea.Right - this.Width;
int y = workingArea.Bottom - this.Height;
// 设置窗体位置
this.Location = new Point(x, y);
} }
/// <summary> /// <summary>
@ -78,31 +142,67 @@ namespace CheckDownload
{ {
try try
{ {
// 读取本地MD5文件信息 // 定义更新流程的总步骤数和当前步骤
const int totalSteps = 6; // 总共6个主要步骤
int currentStep = 0;
// 更新进度条初始状态
UpdateProgressValue(0);
// 步骤1: 读取本地MD5文件信息
UpdateStatus("读取本地MD5文件...");
var localData = await ReadLocalMd5File(); var localData = await ReadLocalMd5File();
if (!ValidateLocalData(localData.Version, localData.Md5, localData.Data)) return; if (!ValidateLocalData(localData.Version, localData.Md5, localData.Data)) return;
UpdateProgressValue(++currentStep * 100 / totalSteps); // 更新进度为16%
// 获取在线MD5文件信息 // 步骤2: 获取在线MD5文件信息
UpdateStatus("获取在线MD5文件信息...");
var onlineData = await GetOnlineMd5File(); var onlineData = await GetOnlineMd5File();
if (!ValidateOnlineData(onlineData.Version, onlineData.Md5)) return; if (!ValidateOnlineData(onlineData.Version, onlineData.Md5)) return;
UpdateProgressValue(++currentStep * 100 / totalSteps); // 更新进度为33%
// 比较版本,判断是否需要更新 // 步骤3: 比较版本,判断是否需要更新
if (!ShouldUpdate(localData.Version, onlineData.Version)) return; UpdateStatus("比较版本信息...");
if (!ShouldUpdate(localData.Version, onlineData.Version))
{
UpdateStatus("当前已是最新版本,无需更新");
UpdateProgressValue(100); // 完成进度
return;
}
UpdateProgressValue(++currentStep * 100 / totalSteps); // 更新进度为50%
// 下载在线MD5文件 // 步骤4: 下载在线MD5文件
UpdateStatus("下载在线MD5文件...");
var onlineFileData = await DownloadOnlineMd5File(); var onlineFileData = await DownloadOnlineMd5File();
if (onlineFileData == null) return; if (onlineFileData == null) return;
UpdateProgressValue(++currentStep * 100 / totalSteps); // 更新进度为66%
// 比较文件差异 // 步骤5: 比较文件差异
UpdateStatus("比较文件差异...");
var differences = CompareDataDifferences(localData.Data, onlineFileData); var differences = CompareDataDifferences(localData.Data, onlineFileData);
UpdateProgressValue(++currentStep * 100 / totalSteps); // 更新进度为83%
// 步骤6: 下载和更新文件
if (differences.Count > 0) if (differences.Count > 0)
{ {
// 下载需要更新的文件 // 下载需要更新的文件
await DownloadUpdatedFiles(differences); await DownloadUpdatedFiles(differences);
} }
else
{
UpdateStatus("无文件需要更新");
}
// 替换本地MD5文件 // 替换本地MD5文件
ReplaceLocalMd5File(); ReplaceLocalMd5File();
UpdateProgressValue(100); // 完成进度
// 显示更新完成消息
UpdateStatus("更新完成");
// 等待1秒后关闭窗体
await Task.Delay(1000);
this.Close();
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -351,8 +451,12 @@ namespace CheckDownload
// 重置下载状态 // 重置下载状态
_completedFiles = 0; _completedFiles = 0;
_downloadedFiles.Clear(); _downloadedFiles.Clear();
Update_Pro.Maximum = differences.Count;
Update_Pro.Value = 0; // 设置总文件数
_totalFilesToDownload = differences.Count;
// 更新状态信息
UpdateStatus($"开始下载更新文件,共{differences.Count}个文件...");
// 创建信号量控制并发下载数量 // 创建信号量控制并发下载数量
var semaphore = new SemaphoreSlim(MaxConcurrentDownloads); var semaphore = new SemaphoreSlim(MaxConcurrentDownloads);
@ -368,6 +472,7 @@ namespace CheckDownload
await Task.WhenAll(downloadTasks); await Task.WhenAll(downloadTasks);
// 所有文件下载完成后统一校验和保存 // 所有文件下载完成后统一校验和保存
UpdateStatus("所有文件下载完成,开始校验和保存...");
VerifyAndSaveAllFiles(); VerifyAndSaveAllFiles();
} }
@ -399,7 +504,6 @@ namespace CheckDownload
{ {
// 更新状态显示当前下载进度 // 更新状态显示当前下载进度
int current = Interlocked.Increment(ref _completedFiles); int current = Interlocked.Increment(ref _completedFiles);
UpdateStatus($"正在下载 ({Update_Pro.Value + 1}/{Update_Pro.Maximum}): {expectedMd5}");
// 下载文件到内存 // 下载文件到内存
using (var client = new HttpClient()) using (var client = new HttpClient())
@ -436,6 +540,8 @@ namespace CheckDownload
// 创建失败文件列表 // 创建失败文件列表
var failedFiles = new List<string>(); var failedFiles = new List<string>();
int processedCount = 0;
int totalFiles = _downloadedFiles.Count;
foreach (var item in _downloadedFiles) foreach (var item in _downloadedFiles)
{ {
@ -445,6 +551,10 @@ namespace CheckDownload
try try
{ {
// 更新状态信息
processedCount++;
UpdateStatus($"正在校验和保存文件 ({processedCount}/{totalFiles}): {relativePath}");
// 计算内存中数据的MD5 // 计算内存中数据的MD5
string actualMd5; string actualMd5;
using (var md5 = MD5.Create()) using (var md5 = MD5.Create())
@ -465,6 +575,16 @@ namespace CheckDownload
// 保存文件 // 保存文件
File.WriteAllBytes(localPath, data); File.WriteAllBytes(localPath, data);
// 更新进度条 - 从95%到98%
// 确保即使在totalFiles为0的情况下也不会出现除零错误并且百分比不会超过98
int percentage = 95;
if (totalFiles > 0)
{
// 计算进度时确保不会超过98%
percentage = 95 + Math.Min(3, (int)(processedCount * 3.0 / totalFiles));
}
UpdateProgressValue(percentage);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -483,6 +603,10 @@ namespace CheckDownload
{ {
throw new Exception($"{failedFiles.Count}个文件校验失败"); throw new Exception($"{failedFiles.Count}个文件校验失败");
} }
else
{
UpdateStatus("所有文件校验和保存成功");
}
} }
/// <summary> /// <summary>
@ -547,14 +671,28 @@ namespace CheckDownload
} }
/// <summary> /// <summary>
/// 更新进度 /// 更新文件下载进度
/// </summary> /// </summary>
private void UpdateProgress() private void UpdateProgress()
{ {
// 在UI线程上更新进度条值 // 计算下载进度百分比,避免除零错误
this.Invoke((MethodInvoker)delegate int percentage = 0;
if (_totalFilesToDownload > 0)
{ {
Update_Pro.Value++; percentage = (int)(_completedFiles * 100.0 / _totalFilesToDownload);
}
// 更新状态信息
UpdateStatus($"正在下载文件 ({_completedFiles}/{_totalFilesToDownload})...");
// 在UI线程上更新进度条值
this.BeginInvoke((MethodInvoker)delegate
{
// 在下载阶段进度从83%到95%之间变化
// 确保不会超过95%
int overallPercentage = 83 + Math.Min(12, (int)(percentage * 12.0 / 100));
// 使用UpdateProgressValue方法确保值在有效范围内
UpdateProgressValue(overallPercentage);
}); });
} }
@ -572,7 +710,7 @@ namespace CheckDownload
} }
// 将下载的在线MD5文件移动到本地MD5文件位置 // 将下载的在线MD5文件移动到本地MD5文件位置
File.Move(_onlineMd5File, LocalMd5File); File.Move(_onlineMd5File, LocalMd5File);
UpdateStatus("已更新本地版本信息"); UpdateStatus("更新完成");
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -586,12 +724,25 @@ namespace CheckDownload
/// <param name="message">要显示的状态信息</param> /// <param name="message">要显示的状态信息</param>
private void UpdateStatus(string message) private void UpdateStatus(string message)
{ {
// 在UI线程上更新状态文本框 // 使用Invoke而不是BeginInvoke确保UI更新完成
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate this.Invoke((MethodInvoker)delegate
{ {
Status_Box.Text = message; Status_Box.Text = message;
// 立即刷新控件,确保状态实时显示
Status_Box.Update();
// 添加Application.DoEvents()调用,确保消息队列被处理
Application.DoEvents();
}); });
} }
else
{
Status_Box.Text = message;
Status_Box.Update();
Application.DoEvents();
}
}
/// <summary> /// <summary>
/// 计算字符串的MD5值 /// 计算字符串的MD5值