在我的WPF项目中。我有:
public partial class MainWindow : Window
{
ObservableCollection<Calls> items = new ObservableCollection<Calls>();
public MainWindow()
{
InitializeComponent();
icTodoList.ItemsSource = items;
this.DataContext = new MainViewModel();
}
icTodoList
是ItemsControl
,我想为其添加两列。
<DockPanel>
<ItemsControl Height="300" Name="icTodoList" ItemsSource="{Binding Calls}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding DialingNumber, Mode=OneWay, FallbackValue=' '}" Grid.Column="0"/>
<TextBlock Text="{Binding DialResult, Mode=OneWay, FallbackValue=' '}" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl></DockPanel>
对于Calls
类,我们有
public class Calls : NotifyUIBase
{
private string dialingNumber;
public string DialingNumber
{
get { return dialingNumber; }
set
{
dialingNumber = value;
RaisePropertyChanged();
}
}
public string dialResult;
public string DialResult {
get { return dialResult; }
set
{
dialResult = value;
RaisePropertyChanged();
}
}
}
NotifyUIBase
继承自INotifyPropertyChanged
,它包含RaisePropertyChanged
属性,忽略此处写入以节省空间。
现在,点击Start
按钮即可获得生产者 - 消费者流程。
private async void Start_Click(object sender, RoutedEventArgs e)
{
var producer = Producer();
var consumer = Consumer();
await Task.WhenAll(producer, consumer);
}
在consumer
方法中,我们更新ItemsControl。
async Task Consumer()
{
try
{
var executionDataflowBlockOptions = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 50
};
var consumerBlock = new ActionBlock<AppointmentReminder>(
remainder =>
{
Calls c = new Calls();
c.DialingNumber = "number..";
c.DialResult = "result...;
items.Add(c);
},
executionDataflowBlockOptions);
bufferBlock.LinkTo(
consumerBlock, new DataflowLinkOptions { PropagateCompletion = true });
await Task.Delay(500);
}
然而我有一个例外:
这种类型的CollectionView不支持从与Dispatcher线程不同的线程更改其SourceCollection。
发生在这一行:
items.Add(C);
我想这是线程问题,所以如何解决?
答案 0 :(得分:4)
您已经将一些选项传递给ActionBlock
;您可以在那里轻松指定UI调度程序:
var executionDataflowBlockOptions = new ExecutionDataflowBlockOptions
{
TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(),
};
请注意,它不再并行运行。如果您的操作代码很简单(创建单个对象并设置几个属性),这应该足够了。但是,如果对象创建是CPU密集型的,您可能希望保持并行性:
var transformOptions = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 50
};
var actionOptions = new ExecutionDataflowBlockOptions
{
TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(),
};
var transformBlock = new TransformBlock<AppointmentReminder, Calls>(reminder =>
{
Calls c = new Calls();
c.DialingNumber = "number..";
c.DialResult = "result...;
return c;
}, transformOptions);
var consumerBlock = new ActionBlock<Calls>(c =>
{
items.Add(c);
}, actionOptions);
var linkOptions = new DataflowLinkOptions { PropagateCompletion = true };
bufferBlock.LinkTo(transformBlock, linkOptions);
transformBlock.LinkTo(consumerBlock, linkOptions);
答案 1 :(得分:1)
只需获取您的项目。在UI线程上执行添加调用:
remainder =>
{
Calls c = new Calls();
c.DialingNumber = "number..";
c.DialResult = "result...;
Dispatcher.Invoke(()=> // Get the dispatcher from your window and use it here
{
items.Add(c);
}
},
答案 2 :(得分:0)
在CP上查看Asynchronous Multi-threaded ObservableCollection结束。
我在我的所有项目中使用它,并且从多个TPL线程中添加项目或项目范围从未出现任何问题......