从另一个Thread更新MainWindow中的进度条

时间:2011-12-11 16:44:34

标签: c# wpf multithreading progress-bar

我知道这个问题已被问过几次,我花了一整天时间试图理解其他答案,但由于我对C#和WPF很新,所以到目前为止没有任何帮助。我将尝试尽可能多地解释我的确切问题,以便直接帮助我。

在我的MainWindow.xaml中,我有一个进度条和一些按钮开始一个新线程和一个长计算:

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

现在在我的MainWindow.xaml.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...
}

现在不知怎的,我必须在我的方法MyLongCalculations中包含一些不断更新progressBar1的东西。但是,我无法让它运作起来。 我知道这一切都很简单,但不幸的是,这是我目前使用C#的水平,所以我希望答案不要太复杂,尽可能详细。

2 个答案:

答案 0 :(得分:3)

后台工作人员很适合这个。

试试这个:

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

答案 1 :(得分:0)

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

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

此外,如果您需要在某个服务/类中传递UI调度程序(如ParametersClass),我建议您查看this nice SO post,了解如何通过具有以下功能的接口来抽象它推送UI同步/异步更改,以便由调用者决定(基本上使用Invoke()BeginInvoke()在UI消息管道中对委托进行排队)。