WPF学习笔记(5)——WPF+Stylet+MVVM:通过线程实现进度条的动态加载,解决UI阻塞问题
问题描述
在winform中,如果要执行一个耗时操作,通常会使用进度条ProgressBar来显示耗时操作的进度,在winform中耗时操作的同时更改进度条的值,在UI上面可以看到进度条动态增长。
但是在WPF中(Stylet框架+MVVM开发模式),如果用同样的方法去实现,则会造成UI阻塞,进度条卡死,当耗时操作执行完毕后UI才刷新(参考下图)。
为什么会这样?
先看一下ViewModel
ViewModel定义了用于表示耗时操作进度的两个属性以及耗时操作,这些属性或者方法会在View中被绑定。
using Stylet;
using System.Threading;
namespace StyletTest.ViewModel
{
public class ProgressBarViewModel:Screen
{
IWindowManager _windowManager;
// 属性:当前值
private int _nowValue;
public int NowValue
{
get
{ return _nowValue; }
set
{ SetAndNotify(ref _nowValue, value); }
}
// 属性:最大值
private int _maxValue;
public int MaxValue
{
get { return _maxValue; }
set { SetAndNotify(ref _maxValue, value); }
}
// 构造函数
public ProgressBarViewModel(IWindowManager windowManager)
{
_windowManager = windowManager;
}
// 事件
public void StartLongTimeOperation()
{
// 执行模拟耗时操作
LongTimeOperation();
}
// 模拟耗时操作
private void LongTimeOperation()
{
// 设置最大值和当前值
MaxValue = 30;
NowValue = 0;
for(int i = 1; i<= MaxValue; i++)
{
// 进度条的当前值递增
NowValue = i;
// 挂起一小会
Thread.Sleep(100);
}
System.Windows.MessageBox.Show("耗时操作执行完毕");
}
}
}
再看一下View
<Window
x:Class="StyletTest.View.ProgressBarView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:StyletTest.View"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:s="https://github.com/canton7/Stylet"
Title="耗时操作"
Width="400"
Height="150"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Command="{s:Action StartLongTimeOperation}"
Content="开始执行耗时操作" />
<Label
Grid.Row="1"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="当前值:" />
<Label
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{Binding NowValue}" />
<Label
Grid.Row="2"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{Binding MaxValue}" />
<Label
Grid.Row="2"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="最大值:" />
<ProgressBar
Grid.Row="3"
Grid.Column="0"
Grid.ColumnSpan="2"
Maximum="{Binding MaxValue}"
Value="{Binding NowValue}" />
</Grid>
</Window>
正常上觉得这样在逻辑上行得通,预期效果应该是随着耗时操作的进行,进度条会跟着增长,但是UI却卡死了。
凡事儿不懂问百度,通过查阅,原因大致是这样的:耗时操作在UI线程中,阻塞了UI线程
解决方案
为耗时操作开启一条新的线程,线程去影响属性值!
代码修改:修改耗时操作(方法)的调用启动方式,开启一个新线程
整体思路
个人学习过程中解决遇到的问题的记录,如有错误烦请指正。