[英]WPF Progress Bar - Update Progress from ClickOnce Upgrade
我已經使用ClickOnce為我的應用程序部署更新了一段時間。 我很高興能夠進行改進,但對當前的進度欄感到有些沮喪。 有一點背景-我有一個XAML窗口類,稱為“ UpdateProgress”,在對該應用程序進行更新時會打開它。 這是我現在正在使用的當前代碼段,它至少會通知用戶正在進行的進度而不會凍結應用程序/崩潰,但是不會直觀地更新進度欄:
case UpdateStatuses.UpdateAvailable:
DialogResult dialogResult = System.Windows.Forms.MessageBox.Show("An update is available. Would you like to update the application now?", "Update available", MessageBoxButtons.OKCancel);
if (dialogResult.ToString() == "OK")
{
BackgroundWorker bgUpdate = new BackgroundWorker();
UpdateProgress updateNotify = new UpdateProgress();
bgUpdate.WorkerReportsProgress = true;
bgUpdate.DoWork += (uptSender, uptE) => { UpdateApplication();};
bgUpdate.ProgressChanged += (progSender, progE) => { updateNotify.updateProgress.Value = progE.ProgressPercentage; };
bgUpdate.RunWorkerCompleted += (comSender, comE) => {
updateNotify.Close();
applicationUpdated();
};
updateNotify.Show();
bgUpdate.RunWorkerAsync();
}
break;
基本上,我在上面創建了一個后台工作程序,它運行以下代碼:
private static void UpdateApplication()
{
try
{
ApplicationDeployment updateCheck = ApplicationDeployment.CurrentDeployment;
//BackgroundWorker bgWorker = new BackgroundWorker();
//UpdateProgress updateNotify = new UpdateProgress();
//updateCheck.UpdateProgressChanged += (s, e) =>
//{
// updateNotify.updateProgress.Value = e.ProgressPercentage;
//};
//bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(UpdateComponent.noteUpdates);
//bgWorker.RunWorkerAsync();
//updateCheck.UpdateCompleted += (s, e) =>
//{
// updateNotify.Close();
// applicationUpdated();
//};
//updateNotify.Dispatcher.InvokeAsync(() =>
// {
//updateNotify.Show();
updateCheck.Update();
//});
//return null;
}
catch (DeploymentDownloadException dde)
{
System.Windows.MessageBox.Show("Cannot install the latest version of the application. Please check your network connection, or try again later. Error: " + dde);
//return null;
}
}
快速說明,當前我僅創建一個名為“ updateCheck”的“ ApplicationDeployment”實例,並讓它在此線程中運行更新。 我之前嘗試過的工作是在下面加載一些注釋的代碼,只是為了查看更新時應用程序崩潰。 事實證明,使用我的應用程序的PROD實例進行調試時,是由於以下錯誤:
調用線程無法訪問該對象,因為其他線程擁有它。
現在,通過做一些挖掘,我已經看到了很多有關此的不錯的讀物。 據我了解,部分問題是我試圖從與MainWindow和其他UI類分開的靜態類中運行此代碼。 我這樣做是為了保持代碼的清潔和模塊化,但是顯然,這是有代價的。 我意識到,如果進度條位於進度條窗口的代碼后面,則可以綁定進度條的進度百分比,但是如果我試圖堅持在我所說的靜態類中運行該進度條怎么辦? 我試過使用Dispatcher方法/ BeginInvoke()之類的方法,但不幸的是最終得到了相同的結果。
有人可以給我關於如何在窗口中用ApplicationDeployment實例的更新例程的進度進度更新進度條進度的最佳建議嗎?
在此先感謝一噸。
您誤解了錯誤原因。 任何UI控件都應該從擁有它的線程中進行更新。
首先,您需要包裝的只是更新進度條的代碼行。
然后,您可以使用IProgress<T>
或Dispatcher
兩種方式包裝呼叫。 前者非常酷,因為基本上您在調用Action<T>
和Progress<T>
可以確保在實例化的同步上下文(例如UI線程)中運行它。 很好,因為基本上您是直接使用WPF Dispatcher將VS抽象化的。
這里有兩種截然不同的方法,首先是在調用方聲明,然后被調用方調用其Report
方法,其次有效地將對UI的調用包裝在被調用方中。
這就是在bgUpdate.ProgressChanged
期間要執行的bgUpdate.ProgressChanged
,需要注意。
現在,如果我是您,我會放棄BackgroundWorker
來使用Task
因為這是現在執行此操作的首選方法,尤其是在WPF中。
使用Task
和IProgress
最小示例:
碼:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel>
<Button Content="DoWork" Click="Button1_Click" />
<ProgressBar Height="20" x:Name="ProgressBar1" Maximum="1.0"/>
</StackPanel>
</Window>
碼:
using System;
using System.Threading.Tasks;
using System.Windows;
namespace WpfApp1
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
private async void Button1_Click(object sender, RoutedEventArgs e)
{
var progress = new Progress<double>(s => { ProgressBar1.Value = s; });
await Task.Run(() => DoWork(progress));
}
private static async Task DoWork(IProgress<double> progress = null)
{
const int count = 100;
for (var i = 0; i < count; i++)
{
await Task.Delay(50);
progress?.Report(1.0d / (count - 1) * i);
}
}
}
}
現在您的代碼甚至不需要了解特定於WPF的Dispatcher
,代碼可以在任何地方,可以更新任何框架。
您也可以使用Task.Run
取消操作:
https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.run?view=netframework-4.7.2
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.