更新绑定到运行长进程的依赖项对象的WPF ProgressBar

时间:2010-10-27 10:48:25

标签: c# wpf multithreading wpf-controls binding

我想将WPF ProgressBar的value属性绑定到在长时间运行的进程中更新的依赖项属性。如果从主线程调用长时间运行的进程,则会阻止UI(以及ProgressBar)进行更新,直到进程完成为止 - 阻止通过显示进程的所需进度。长时间运行的进程也无法通过旋转单独的线程来运行,因为无法将依赖项属性从不同的线程更新为其所有者(即创建它的线程)。

在下面的代码中,单击按钮时,长时间运行的进程会运行,进度条会在完成时从0%跳到100%。相反,我希望能够单击按钮并让进度条显示长时间运行进度的进度(即,当进程结束但显示顺利进展时,不仅仅是从0%更新到100%)。

MainWindow.xaml

<Window x:Class="ProgressBarTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <StackPanel>
        <Button Width="200" Height="50" x:Name="btnRun" Click="btnRun_Click">Run Process</Button>
        <ProgressBar Width="200" Height="20" x:Name="pbProgress" Minimum="0" Maximum="100" Value="{Binding Path=MyFoo.ProgressValue}"/>
    </StackPanel>
</Window>

MainWindow.xaml.cs

using System.Windows;
using System.Threading;

namespace ProgressBarTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public Foo MyFoo { get; set; }

        public MainWindow()
        {
            MyFoo = new Foo();
            InitializeComponent();

        }

        private void btnRun_Click(object sender, RoutedEventArgs e)
        {
            btnRun.IsEnabled = false;

            MyFoo.LongRunningProcess();  // Since this runs on same thread as UI, progress bar does not update until the long running process completes.

            btnRun.IsEnabled = true;
        }
    }

    public class Foo : DependencyObject
    {
        public static readonly DependencyProperty ProgressValueProperty = DependencyProperty.Register("ProgressValue", typeof(double), typeof(Foo));
        public double ProgressValue
        {
            get { return (double)GetValue(ProgressValueProperty); }
            set
            {
                SetValue(ProgressValueProperty, value);
            }
        }

        public Foo()
        {
            ProgressValue = 0;
        }

        public void LongRunningProcess()
        {
            do
            {
                ProgressValue += 1;

                Thread.Sleep(30);
            }
            while (ProgressValue < 100);
        }
    }
}

P.S。我知道有一种方法可以通过将ProgressBar实例作为参数传递给长时间运行的进程来实现这一点,以便它可以直接通过Dispatcher.Invoke更新它,但这不是我想要的。我希望通过绑定到依赖项属性来更新进度条。

由于

肖恩

3 个答案:

答案 0 :(得分:2)

我会尝试比欣快更好。

您需要使用在不同线程上运行该进程的BackgroundWorker运行LongRunningProcess,然后使用ProgressChanged事件更新属性

// This event handler updates the progress bar.
private void backgroundWorker1_ProgressChanged(object sender,
    ProgressChangedEventArgs e)
{
    MyFoo.ProgressValue = e.ProgressPercentage;
}

答案 1 :(得分:2)

需要从主线程更新UI。因此,您需要使用Dispather来更新属性。

在LongRunningProcess()中使用它来将调用放在Dispatcher队列上:

Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Render, new Action<double>(UpdateData), value);

并实现更新方法如下:

private void UpdateData(double value){
   MyFoo.ProgressValue = value;
}

希望这有帮助。

答案 2 :(得分:1)

我不知道如果你找到了解决问题的方法,但这就是我过去解决类似问题的方法。这使您可以绑定到DP并只更新正常属性

public static readonly DependencyProperty progressProperty = DependencyProperty.Register("progress", typeof(int), typeof(this));
public int progress
    {
        get
        {
            return (int)this.Dispatcher.Invoke(
                   System.Windows.Threading.DispatcherPriority.Background,
                   (DispatcherOperationCallback)delegate { return GetValue(progressProperty ); },
                   progressProperty );
        }
        protected set
        {
            this.Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate { SetValue(progressProperty , value); }, value);
        }
    }