C# MVVM 中的简单进度条
Simple progress bar in C# MVVM
我在公司使用LabVIEW,想换成C# MVVM。在我的自学之旅中,当遇到一些在 LabVIEW 中很容易完成但在 C# 中根本无法完成的事情时,我觉得自己像个彻头彻尾的白痴。
在了解了 MVVM 基础知识之后,我现在正在尝试获得一个简单的进度条,以 1000 毫秒的间隔显示更新,直到完成为止。但是当执行下面的代码时,window 显示一个空的进度条,并在 4 秒后跳转到最终值 100。
我想我有一些绝对的基本编程原则是错误的,所以如果你能帮助我,我将不胜感激。提前致谢!!
代码如下:
MainWindow.xaml
<Window x:Class="MicrosoftMVVMToolkit_Sandbox.MainWindow"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MicrosoftMVVMToolkit_Sandbox"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ProgressBar Width="500" Height="10" Value="{Binding Progress}" Minimum="0" Maximum="100"></ProgressBar>
</Grid>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Toolkit.Mvvm;
namespace MicrosoftMVVMToolkit_Sandbox
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ViewModel vm = new ViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = vm;
this.Show();
vm.Work();
}
}
}
ViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Toolkit.Mvvm;
using Microsoft.Toolkit.Mvvm.ComponentModel;
namespace MicrosoftMVVMToolkit_Sandbox
{
class ViewModel : ObservableObject
{
private int progress;
public int Progress
{
get => progress;
set => SetProperty(ref progress, value);
}
public void Work()
{
Progress = 0;
System.Threading.Thread.Sleep(1000);
Progress = 25;
System.Threading.Thread.Sleep(1000);
Progress = 50;
System.Threading.Thread.Sleep(1000);
Progress = 75;
System.Threading.Thread.Sleep(1000);
Progress = 100;
}
}
}
==============================
解决方案 1 - 等待 Task.Delay 选项:
这里是 await Task.Delay 选项的示例代码,以防其他人遇到同样的问题:
public async void Work()
{
Progress = 0;
await Task.Delay(1000);
Progress = 25;
await Task.Delay(1000);
Progress = 50;
await Task.Delay(1000);
Progress = 75;
await Task.Delay(1000);
Progress = 100;
}
这里的主要问题是进度条只能在 UI 线程未被阻塞时才能更新。 IE。它正在处理 windows 条消息。然而,Thread.Sleep
会阻塞 UI 线程,阻止它做任何其他工作,包括绘制进度条。
有几个可能的解决方案
- 使用异步方法并使用
await Task.Delay(1000)
而不是 Thread.Sleep
。这将允许 UI 线程在等待时做其他工作。但是如果你需要做任何处理密集型的事情,它就不会工作。
- 运行 您在后台线程上的工作,例如
Task.Run
。但这会导致下一个问题:不允许从后台线程更新任何 UI 控件。为此,有两种解决方案:
- 使用
Dispatcher.BeginInvoke
从UI线程更新Progress-属性。 (或 UI 线程上 运行 代码的任何等效方法)
- 从后台线程更新进度字段,但使用单独的计时器为 Progress-属性 引发
OnPropertyChanged
事件。计时器应该是 UI 线程上 运行 的计时器,例如 DispatchTimer
。这样做的好处是允许频繁更新进度,而不用向 UI 线程发送垃圾消息。
在构造函数中:
public MainWindow()
{
InitializeComponent();
this.DataContext = vm;
this.Show();
vm.Work();
}
vm.Work()
阻止您的代码执行 4 秒(每次调用 System.Threading.Thread.Sleep(1000)
)
让UI对那个时间不负责任
您需要使用一些非阻塞工具更改进度条 Progress
,例如 DispatcherTimer
。
示例:
// remove vm.Work() from constructor
// add private field in MainWindow
System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
// add in constructor
dispatcherTimer.Tick += dispatcherTimer_Tick;
dispatcherTimer.Interval = new TimeSpan(0,0,1);
dispatcherTimer.Start();
// add private method in MainWindow
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
vm.Progress += 1;
}
我没有测试这段代码。
我在公司使用LabVIEW,想换成C# MVVM。在我的自学之旅中,当遇到一些在 LabVIEW 中很容易完成但在 C# 中根本无法完成的事情时,我觉得自己像个彻头彻尾的白痴。
在了解了 MVVM 基础知识之后,我现在正在尝试获得一个简单的进度条,以 1000 毫秒的间隔显示更新,直到完成为止。但是当执行下面的代码时,window 显示一个空的进度条,并在 4 秒后跳转到最终值 100。
我想我有一些绝对的基本编程原则是错误的,所以如果你能帮助我,我将不胜感激。提前致谢!!
代码如下:
MainWindow.xaml
<Window x:Class="MicrosoftMVVMToolkit_Sandbox.MainWindow"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MicrosoftMVVMToolkit_Sandbox"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ProgressBar Width="500" Height="10" Value="{Binding Progress}" Minimum="0" Maximum="100"></ProgressBar>
</Grid>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Toolkit.Mvvm;
namespace MicrosoftMVVMToolkit_Sandbox
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ViewModel vm = new ViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = vm;
this.Show();
vm.Work();
}
}
}
ViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Toolkit.Mvvm;
using Microsoft.Toolkit.Mvvm.ComponentModel;
namespace MicrosoftMVVMToolkit_Sandbox
{
class ViewModel : ObservableObject
{
private int progress;
public int Progress
{
get => progress;
set => SetProperty(ref progress, value);
}
public void Work()
{
Progress = 0;
System.Threading.Thread.Sleep(1000);
Progress = 25;
System.Threading.Thread.Sleep(1000);
Progress = 50;
System.Threading.Thread.Sleep(1000);
Progress = 75;
System.Threading.Thread.Sleep(1000);
Progress = 100;
}
}
}
==============================
解决方案 1 - 等待 Task.Delay 选项:
这里是 await Task.Delay 选项的示例代码,以防其他人遇到同样的问题:
public async void Work()
{
Progress = 0;
await Task.Delay(1000);
Progress = 25;
await Task.Delay(1000);
Progress = 50;
await Task.Delay(1000);
Progress = 75;
await Task.Delay(1000);
Progress = 100;
}
这里的主要问题是进度条只能在 UI 线程未被阻塞时才能更新。 IE。它正在处理 windows 条消息。然而,Thread.Sleep
会阻塞 UI 线程,阻止它做任何其他工作,包括绘制进度条。
有几个可能的解决方案
- 使用异步方法并使用
await Task.Delay(1000)
而不是Thread.Sleep
。这将允许 UI 线程在等待时做其他工作。但是如果你需要做任何处理密集型的事情,它就不会工作。 - 运行 您在后台线程上的工作,例如
Task.Run
。但这会导致下一个问题:不允许从后台线程更新任何 UI 控件。为此,有两种解决方案:- 使用
Dispatcher.BeginInvoke
从UI线程更新Progress-属性。 (或 UI 线程上 运行 代码的任何等效方法) - 从后台线程更新进度字段,但使用单独的计时器为 Progress-属性 引发
OnPropertyChanged
事件。计时器应该是 UI 线程上 运行 的计时器,例如DispatchTimer
。这样做的好处是允许频繁更新进度,而不用向 UI 线程发送垃圾消息。
- 使用
在构造函数中:
public MainWindow()
{
InitializeComponent();
this.DataContext = vm;
this.Show();
vm.Work();
}
vm.Work()
阻止您的代码执行 4 秒(每次调用 System.Threading.Thread.Sleep(1000)
)
让UI对那个时间不负责任
您需要使用一些非阻塞工具更改进度条 Progress
,例如 DispatcherTimer
。
示例:
// remove vm.Work() from constructor
// add private field in MainWindow
System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
// add in constructor
dispatcherTimer.Tick += dispatcherTimer_Tick;
dispatcherTimer.Interval = new TimeSpan(0,0,1);
dispatcherTimer.Start();
// add private method in MainWindow
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
vm.Progress += 1;
}
我没有测试这段代码。