从不同的线程更新主窗口中的进度条

本文关键字:窗口 更新 线程 | 更新日期: 2023-09-27 18:17:25

我知道这个问题已经被问过好几次了,我花了一整天的时间试图理解其他答案,但是因为我对c#和WPF非常陌生,所以到目前为止没有任何帮助。我将尽我所能解释我的确切问题,这样它将直接帮助我。

在我的主窗口。我有一个进度条和一些按钮开始一个新的线程和一个长计算:

<ProgressBar Height="....... Name="progressBar1"/>
<Button Content="Button" Name="button1" Click="button1_Click" />

现在在我的mainwindow . example .cs:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
    private void button1_Click(object sender, RoutedEventArgs e)
    {
        Thread thread = new Thread(new ParameterizedThreadStart(MyLongCalculation));
        ParameterClass myParameters = new ParameterClass();
        thread.Start(myParameters);
    }
    public void MyLongCalculations(object myvalues)
    {
        ParameterClass values = (ParameterClass)myvalues;
        //some calculations
    }
}
public class ParameterClass
{
    //public variables...
}

现在不知何故,我必须在我的方法mylongcalculation中包含一些东西,它将不断更新progressBar1。然而,我就是不能让它工作。我知道这一切都很简单,但不幸的是,这是我目前对c#的水平,所以我希望答案不要太复杂,尽可能详细。

从不同的线程更新主窗口中的进度条

Background worker非常适合这个。

试试这个:

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        // Initialize UI
        InitializeComponent();
        // Process data
        ProcessDataAsync(new ParameterClass { Value = 20 });
    }
    /// <summary>
    /// Processes data asynchronously
    /// </summary>
    /// <param name="myClass"></param>
    private void ProcessDataAsync(ParameterClass myClass)
    {
        // Background worker
        var myWorker = new BackgroundWorker
        {
            WorkerReportsProgress = true,
        };
        // Do Work
        myWorker.DoWork += delegate(object sender, DoWorkEventArgs e)
        {
            // Set result
            e.Result = MyLongCalculations(myClass);
            // Update progress (50 is just an example percent value out of 100)
            myWorker.ReportProgress(50);
        };
        // Progress Changed
        myWorker.ProgressChanged += delegate(object sender, ProgressChangedEventArgs e)
        {
            myProgressBar.Value = e.ProgressPercentage;
        };
        // Work has been completed
        myWorker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
        {
            // Work completed, you are back in the UI thread.
            TextBox1.Text = (int) e.Result;
        };
        // Run Worker
        myWorker.RunWorkerAsync();
    }
    /// <summary>
    /// Performs calculations
    /// </summary>
    /// <param name="myvalues"></param>
    /// <returns></returns>
    public int MyLongCalculations(ParameterClass myvalues)
    {
        //some calculations
        return (myvalues.Value*2);
    }
}
/// <summary>
/// Custom class
/// </summary>
public class ParameterClass
{
    public int Value { get; set; }
}

您可以使用Dispatcher.BeginInvoke()将UI更改推送到UI线程而不是工作线程。最重要的是——你需要访问Dispatcher,它与UI线程相关联,而不是你手动创建的工作线程。所以我建议缓存Dispatcher.Current,然后在一个工作线程中使用,你可以通过ParametersClass或只是在类级别上声明一个dispatchr字段。

public partial class MainWindow
{
    private Dispatcher uiDispatcher;
    public MainWindow()
    {
        InitializeComponents();
        // cache and then use in worker thread method
        this.uiDispatcher = uiDispatcher;
    }
    public void MyLongCalculations(object myvalues)
    {
        ParameterObject values = (ParameterObject)myvalues;
        this.uiDispatcher.BeginInvoke(/*a calculations delegate*/);
    }
}

另外,如果你需要在一些服务/类(如ParametersClass)中传递一个UI调度程序,我建议看看这个漂亮的SO帖子,它展示了如何通过一个接口抽象它,能够同步/异步地推送UI更改,因此它将取决于调用者(基本上使用Invoke()BeginInvoke()在UI消息管道中排队委托)。