WPF进度栏-ClickOnce升级的更新进度

时间:2019-04-12 01:26:37

标签: c# wpf progress-bar clickonce auto-update

一段时间以来,我一直在使用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实例的更新例程的进度百分比更新窗口中进度条进度的最佳建议吗?

提前感谢一吨。

1 个答案:

答案 0 :(得分:1)

您误解了错误原因。任何UI控件都应从拥有它的线程中进行更新。

首先,您需要包装的只是更新进度条的代码行。

然后,您可以使用两种方式来打包呼叫,即使用IProgress<T>Dispatcher。前者非常酷,因为基本上您正在调用Action<T>Progress<T>可以确保在实例化的同步上下文中运行它,例如UI线程。很好,因为基本上您是直接使用WPF Dispatcher将VS抽象化的。

这里有两种截然不同的方法,首先是在调用方声明,然后被调用方调用其Report方法,其次有效地将对UI的调用包装在被调用方中。

这就是您在bgUpdate.ProgressChanged期间要执行的操作。

现在,如果我是您,我会放弃BackgroundWorker来支持Task,因为这是现在这样做的首选方法,尤其是在WPF中。

使用TaskIProgress的最小示例:

代码:

<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);
            }
        }
    }
}

enter image description here

现在,您甚至不需要编写有关WPF特定的Dispatcher的代码,代码可以在任何地方,可以更新任何框架。

您也可以使用Task.Run取消操作:

https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.run?view=netframework-4.7.2

相关问题