我有一些代码尝试使用如下方法设置WPF中进度条的值:
private void SetPercentage(int channel, int percentage)
{
switch(channel)
{
case 1:
ch1Bar.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Send, new DelegateMethod(() => ch1Bar.Value = (double)percentage));
break;
case 2:
ch2Bar.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Send, new DelegateMethod(() => ch2Bar.Value = (double)percentage));
break;
case 3:
ch3Bar.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Send, new DelegateMethod(() => ch3Bar.Value = (double)percentage));
break;
case 4:
ch4Bar.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Send, new DelegateMethod(() => ch4Bar.Value = (double)percentage));
break;
case 5:
ch5Bar.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Send, new DelegateMethod(() => ch5Bar.Value = (double)percentage));
break;
case 6:
ch6Bar.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Send, new DelegateMethod(() => ch6Bar.Value = (double)percentage));
break;
case 7:
ch7Bar.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Send, new DelegateMethod(() => ch7Bar.Value = (double)percentage));
break;
case 8:
ch8Bar.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Send, new DelegateMethod(() => ch8Bar.Value = (double)percentage));
break;
}
}
每个chXBar
,其中X是1到8,是一个单独的进度条。在另一个线程(使用Thread
类手动创建)中的循环中调用此方法。循环一次设置一个通道值,并且非常慢(使用Thread.Sleep
来减慢它)。
然而,这些调用似乎都不起作用;进度条上的值不会改变(它们始终保持为零)。代码编译很好,调试中没有抛出异常。我真的不想写8个代表和8个方法来使用它们。
有没有人有任何指示?
(P.S。我在Windows 7 x64上使用.NET 4.5)
答案 0 :(得分:1)
下面是一个最小的演示,演示如何使用绑定和INotifyPropertyChanged在另一个线程上更改值时更新显示。与最佳做法有一些偏差,以保持简单。
XAML:
<Window x:Class="WPFThreadDemo.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>
<StackPanel>
<ProgressBar Value="{Binding Channel1}" Height="100"/>
<ProgressBar Value="{Binding Channel2}" Height="100"/>
</StackPanel>
</Grid>
</Window>
在xaml中,每个进度条的Value属性绑定到数据上下文中的属性,该属性提供要显示的值。每次数据上下文为属性引发NotifyPropertyChanged时,将从数据上下文更新进度条值。
C#:
这里我们有一个简单的模型类来表示进度条显示的数据,以及一个更新模型的工人。窗口后面的代码实例化一个新模型,用它来创建一个新工人,然后启动工作进程。
我使用字典来存储值,以减少添加新频道所需的复制粘贴量。它不是必需的;每个人都可以用具有自己的支持字段的属性来表示。
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
using System.Windows;
namespace WPFThreadDemo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Create a new model and set the data context to point at it
Model model = new Model();
this.DataContext = model;
// Set up a new worker to do some work on another thread and update the mode
Worker worker = new Worker(model);
worker.DoWork(100);
}
}
public class Model : INotifyPropertyChanged
{
// Implementation of INotifyPropertyChanged
// The progress bar will be hooked up to this event by the binding
// When the event is raised with a name used in a binding, the model is queried
// for the new value and the display is updated
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
var eventArgs = new PropertyChangedEventArgs(propertyName);
handler(this, eventArgs);
}
}
// Store for the channel values
Dictionary<int, int> _channels = new Dictionary<int, int>();
// Simple wrapper around the dictionary which creates a default value of 0 if it doesn't exist yet
private int GetValue(int channel)
{
if (!_channels.ContainsKey(channel))
{
_channels[channel] = 0;
}
return _channels[channel];
}
// If the value is new or has changed, store it and raise property changed notification to update display
public void SetValue(int channel, int value)
{
int oldValue;
bool valueExists = _channels.TryGetValue(channel, out oldValue);
// nothing to do if the value already exists and it hasn't changed
if (valueExists && oldValue == value) return;
_channels[channel] = value;
RaisePropertyChanged("Channel" + channel.ToString());
}
// WPF binding works against public properties so we need to provide properties to bind against
public int Channel1
{
get
{
return GetValue(1);
}
set
{
SetValue(1, value);
}
}
public int Channel2
{
get
{
return GetValue(2);
}
set
{
SetValue(2, value);
}
}
}
// Simple worker mock which updates the model values on another thread
public class Worker
{
Model _valueStore;
public Worker(Model valueStore)
{
_valueStore = valueStore;
}
public void DoWork(int duration)
{
ThreadPool.QueueUserWorkItem(
(x) =>
{
for (int channel = 0; channel < 2; channel++)
{
for (int value = 0; value < 100; value++)
{
_valueStore.SetValue(channel + 1, value + 1);
Thread.Sleep(duration);
}
}
});
}
}
}
通常,我会为worker和model使用一个接口,并使用当前项目中使用的任何IOC容器来创建/注入它们,以消除紧耦合并提高可测试性,但是这个示例设计得如此简单尽可能。
答案 1 :(得分:0)
如果您正在使用WPF,那么您确实应该将进度条值绑定到变量。 WPF自动处理跨线程的INotifyPropertyChanged事件,而不是INotifyCollectionChanged。