C# 在MVVM的主线程中更新进度条

C# 在MVVM的主线程中更新进度条,c#,wpf,multithreading,mvvm,dispatcher,C#,Wpf,Multithreading,Mvvm,Dispatcher,在我的应用程序中,我执行了一个较长的操作,我希望显示操作的进度。在长期操作中,我使用第三方dll。不幸的是,dll不支持来自非主线程的调用。因此,我无法使用其他线程来启动进程 我找到了一种使用Dispather更新主线程中进度条的方法。起初,我编写了一个简单的WPF应用程序,并在代码隐藏中编写了简单的方法 private void ButtonBase_OnClick(object sender, RoutedEventArgs e) { for (int i = 0; i <=

在我的应用程序中,我执行了一个较长的操作,我希望显示操作的进度。在长期操作中,我使用第三方dll。不幸的是,dll不支持来自非主线程的调用。因此,我无法使用其他线程来启动进程

我找到了一种使用Dispather更新主线程中进度条的方法。起初,我编写了一个简单的WPF应用程序,并在代码隐藏中编写了简单的方法

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    for (int i = 0; i <= 100; i++)
    {
        Dispatcher.Invoke(DispatcherPriority.Loaded,
                            (Action)(() =>
                                {
                                    pb.Value = i;
                                }));
        Thread.Sleep(10);
    }
}
我用XAML写的

<ProgressBar                   tesWpfAppMvvm:ProgressBarAttachedBehavior.ValueAsync="{Binding Progress}"/>
我到处都想找到解决办法,但找不到。无论在何处,解决方案都是在单独的线程中运行日志进程

请告诉我哪里是我的错误以及如何解决我的问题

您可以下载演示项目来重现问题

您可以检查相同的异常吗?根据文档,您可以使用另一个委托包装“ValueAsyncChanged”事件处理程序,并使用Dispatcher.BeginInvoke方法调用“ValueAsyncChanged”。WPF引擎似乎不允许在忙于加载时执行PushFrame调用

    public static readonly DependencyProperty ValueAsyncProperty =
    DependencyProperty.RegisterAttached("ValueAsync", 
        typeof (double), 
        typeof (ProgressBarAttachedBehavior), 

        new UIPropertyMetadata(default(double),
(o, e) => 
Dispatcher.BeginInvoke( 
new DependencyPropertyChangedEventHandler( ValueAsyncChanged), o, e);));

为什么不直接从视图模型中使用
Application.Current.Dispatcher.Invoke()

请看一下这个示例:

MainViewModel.cs

使用系统;
使用系统组件模型;
使用系统线程;
使用System.Windows;
使用System.Windows.Threading;
命名空间WpfApplication4
{
公共类MainViewModel:INotifyPropertyChanged
{
公共事件属性ChangedEventHandler属性已更改
=委托{};
私人国际计算器;
公共整数计数器
{
获取{return mCounter;}
设置
{
mCounter=值;
PropertyChanged(新PropertyChangedEventArgs(“计数器”);
}
}
/// 
///应该从后台线程运行
/// 
公开作废开始()
{
对于(int i=0;i
{
//主线程中的长时间运行操作
//低优先级以防止UI冻结
睡眠(100);
计数器=i;
}));
}
}
}
}
MainWindow.xaml.cs

使用System.Threading.Tasks;
使用System.Windows;
命名空间WpfApplication4
{
公共部分类主窗口:窗口
{
私有MainViewModel MainViewModel;
公共主窗口()
{
初始化组件();
已加载+=(发送方,参数)=>开始操作();
}
私有无效按钮\u单击(对象发送者,路由目标e)
{
起始操作();
}
/// 
///在后台启动长时间运行的操作。
/// 
私有无效开始操作()
{
DataContext=mainViewModel=new mainViewModel();
Task.Factory.StartNew(()=>mainViewModel.Start());
}
}
}
以及主窗口.xaml

<Window x:Class="WpfApplication4.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">
    <Grid>
        <ProgressBar Height="20" Width="200" Value="{Binding Counter}" />
        <Button Content="Change View model" Height="23" Margin="0,100,0,0"
                HorizontalAlignment="Center"
                Click="Button_Click" />
    </Grid>
</Window>

以下是您想要的解决方案: 设置progressbar可见性,然后为UI线程提供足够的时间来更新其状态

注:

当UI线程从睡眠中醒来时,由于UI密集型进程的执行,应用程序将变得无响应

一旦UI密集型流程完成,应用程序将再次响应

下面是一个代码示例:

XAML:

<telerik:RadProgressBar x:Name="progress"  
Visibility="{Binding ProgressVisibility, Mode=OneWay}" IsIndeterminate="True"  
const int MINIMUM_UI_WAIT_REQUIRED = 2;

ProgressVisibility = Visibility.Visible;
await Task.Factory.StartNew(() => { Thread.Sleep(MINIMUM_UI_WAIT_REQUIRED); });

维克托,如果你的问题仍然适用,你介意考虑我的答案吗?
<Window x:Class="WpfApplication4.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">
    <Grid>
        <ProgressBar Height="20" Width="200" Value="{Binding Counter}" />
        <Button Content="Change View model" Height="23" Margin="0,100,0,0"
                HorizontalAlignment="Center"
                Click="Button_Click" />
    </Grid>
</Window>
<telerik:RadProgressBar x:Name="progress"  
Visibility="{Binding ProgressVisibility, Mode=OneWay}" IsIndeterminate="True"  
const int MINIMUM_UI_WAIT_REQUIRED = 2;

ProgressVisibility = Visibility.Visible;
await Task.Factory.StartNew(() => { Thread.Sleep(MINIMUM_UI_WAIT_REQUIRED); });