Обновите WPF ProgressBar, связанный с объектом зависимости, выполняющим длинный процесс - PullRequest
3 голосов
/ 27 октября 2010

Я хочу связать свойство value WPF ProgressBar со свойством зависимостей, которое обновляется во время длительного процесса. Если длительный процесс вызывается из основного потока, это блокирует обновление пользовательского интерфейса (и, следовательно, 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, но это не то, что я хочу. Я хочу, чтобы индикатор выполнения обновлялся через привязку к свойству зависимости.

Спасибо

Sean

Ответы [ 3 ]

2 голосов
/ 05 января 2011

Пользовательский интерфейс должен быть обновлен из основного потока.Следовательно, вам необходимо использовать Диспетчер для обновления свойства.

Используйте это из LongRunningProcess (), чтобы поместить вызов в очередь Диспетчера:

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

и реализовать метод обновлениявот так:

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

Надеюсь, это поможет.

2 голосов
/ 27 октября 2010

Я постараюсь быть лучше Эйфорика.

Вам необходимо запустить LongRunningProcess, используя BackgroundWorker , который запускает процесс в другом потоке, а затем обновить свойство, используя ProgressChanged событие

// This event handler updates the progress bar.
private void backgroundWorker1_ProgressChanged(object sender,
    ProgressChangedEventArgs e)
{
    MyFoo.ProgressValue = e.ProgressPercentage;
}
1 голос
/ 30 марта 2011

Я не знаю, если вы нашли решение своей проблемы, но именно так я решил симуляционную проблему в прошлом. Это позволяет вам связываться с вашим 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);
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...