我在wpf窗口中有一个列表框,它绑定到viewmodel对象中的列表。当我在viewmodel对象中运行一个方法时,它处理列表的成员,每个成员都有一个进度。我想在执行期间不断更新gui。就像现在一样,它只在处理完成后更新gui。
在这里,我试图创建一个我现在所拥有的小例子:
MainWindow.xaml:
<Window x:Class="WPF_MVVM_Thread_Progressbar.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPF_MVVM_Thread_Progressbar"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:TestViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8*"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="8*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox Grid.Column="0" Grid.Row="0" Margin="5" ItemsSource="{Binding TestWorker.TestList}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<ProgressBar Minimum="0" Maximum="100" Value="{Binding Progress, Mode=OneWay}" Background="Bisque">
<ProgressBar.Style>
<Style TargetType="{x:Type ProgressBar}">
<Style.Triggers>
<DataTrigger Binding="{Binding Progress}" Value="0">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ProgressBar.Style>
</ProgressBar>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" Background="Transparent"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Grid.Column="0" Grid.Row="1" Content="TestRun" Command="{Binding TestRunCommand}"></Button>
<TextBlock Text="{Binding SelectedIdx}" Grid.Column="1" Grid.Row="1"/>
</Grid>
</Window>
MainWindowl.xaml.cs:
using Prism.Commands;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace WPF_MVVM_Thread_Progressbar
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class TestViewModel : INotifyPropertyChanged
{
private WorkingClass _testWorker;
private DelegateCommand _testRunCommand;
public DelegateCommand TestRunCommand
{
get { return _testRunCommand; }
set { _testRunCommand = value; }
}
public WorkingClass TestWorker
{
get { return _testWorker; }
set { _testWorker = value; RaisePropertyChanged("TestWork"); }
}
private int _selectedIdx;
public int SelectedIdx
{
get { return _selectedIdx; }
set { _selectedIdx = value; RaisePropertyChanged("SelectedIdx"); }
}
public TestViewModel()
{
_testWorker = new WorkingClass();
_testRunCommand = new DelegateCommand(TestRun, canRun);
}
public async void TestRun()
{
//await Task.Run(() => _testWorker.Work());
_testWorker.Work();
}
private bool canRun()
{
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class WorkingClass : INotifyPropertyChanged
{
private ObservableCollection<TestObject> _testList;
public ObservableCollection<TestObject> TestList
{
get { return _testList; }
set { _testList = value; RaisePropertyChanged("TestList"); }
}
public WorkingClass()
{
_testList = new ObservableCollection<TestObject>();
_testList.Add(new TestObject("Object A"));
_testList.Add(new TestObject("Object B"));
_testList.Add(new TestObject("Object C"));
RaisePropertyChanged("TestList");
}
public void Work()
{
foreach (var obj in TestList)
{
obj.TestWork();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class TestObject : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private int _progress;
public int Progress
{
get { return _progress; }
set { _progress = value; RaisePropertyChanged("Progress"); }
}
public TestObject(string name)
{
this._name = name;
_progress = 0;
}
public void TestWork()
{
for (int i = 0; i < 100; i++)
{
System.Threading.Thread.Sleep(10);
Progress++;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
我曾尝试使用ObservableCollection和INotifyPropertyChanged,但这似乎还不够。
最终我希望能够使用来自TestViewModel.TestRun()的async / await调用获得相同的效果。
有人可能会就此提供一些见解吗?非常感谢。
答案 0 :(得分:1)
我认为您在UI完成后只更新UI的当前原因是您在UI线程上运行所有这些。我会尝试这样做:
Task.Run(async delegate
{
await _testWorker.Work();
});
或
Task.Run(() =>
{
_testWorker.Work();
});
或者
Task.Factory.StartNew(() =>
{
_testWorker.Work();
});
或者
var newThread = new Thread(new ThreadStart(_testWorker.Work());
newThread.Start();
这将立即返回到UI,但允许您的代码继续。
注意:您必须小心使用UI线程中的对象。 ObservableCollections只能在与处理UI工作的调度程序相同的线程上创建。如果您使用双向绑定,则必须小心线程安全性。
答案 1 :(得分:0)
我过去使用BackgroundWorker
成功完成了此操作。
public class TestObject : INotifyPropertyChanged {
private BackgroundWorker worker;
public TestObject() {
worker = new BackgroundWorker() {
WorkerReportsProgress = true
};
worker.DoWork += DoWork;
worker.ProgressChanged += WorkProgress;
worker.RunWorkerCompleted += WorkFinished;
}
public int Progress
{
get { return _progress; }
set { _progress = value; RaisePropertyChanged("Progress"); }
}
// Begin doing work
public void TestWork() {
worker.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs eventArgs) {
worker.ReportProgress(0, "Work started");
for (int i = 0; i < 100; i++) {
System.Threading.Thread.Sleep(10);
worker.ReportProgress(i, "Message");
}
}
// Fires when the progress of a job changes.
private void WorkProgress(object sender, ProgressChangedEventArgs e) {
// Do something with the progress here
Progress = e.ProgressPercentage;
}
// Fires when a job finishes.
private void WorkFinished(object sender, RunWorkerCompletedEventArgs e) {
// The work finished. Do something?
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
// NOTE: If you're running C#6 use the null conditional operator for this check.
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(e));
}
}
BackgroundWorker
基本上在一个单独的线程上运行所有内容,并在其进度发生变化或完成工作时报告。您可以从进度报告中提取ProgressPercentage并在UI中使用它。希望有所帮助。为了使示例简单,我没有包含您的一些代码,但这应该让您知道如何完成它。