修改WPF中另一个线程的列表?

时间:2015-05-11 20:04:03

标签: c# wpf multithreading

我有一个带有MainWindow的WPF应用程序,它将处理消息的输出。我在窗口中设置了一个消息列表,如下所示:

public partial class MainWindow : Window
{
    public ConcurrentBag<ViewMessage> MessageList
    {
        get { return (ConcurrentBag<ViewMessage>)GetValue(MessageListProperty); }
        set { SetValue(MessageListProperty, value); }
    }

    public static readonly DependencyProperty MessageListProperty = DependencyProperty.Register("MessageList", typeof(ConcurrentBag<ViewMessage>), typeof(MainWindow), new PropertyMetadata(null));

    public MainWindow()
    {
        InitializeComponent();
    }

    public void ShowMessage(string header, string message)
    {
        ViewMessage message = new ViewMessage("Sucess", "Example Message");
        MessageList.Add(message);
    }
}

消息的样式设置为在一段时间(3秒)后淡出,我想在此之后从集合中删除该项。我不知道是否可以只在XAML中进行,如果是,我该怎么做?

我尝试以编程方式执行此操作:

public MainWindow()
{
    Timer timer = new Timer();
    timer.Interval = 1000;
    timer.Elapsed += new ElapsedEventHandler(MessageCheckTick);
    timer.Start();

    //Tried a Thread.Start() as well
}

private void MessageCheckTick(object sender, ElapsedEventArgs e)
{
    for (int i = MessageList.Count - 1; i >= 0; i++)
    {
        ViewMessage message = null;
        if (MessageList.TryPeek(out message))
        {
            if (message.DateMessage >= DateTime.Now.AddMilliseconds(3000))
            { MessageList.TryTake(out message); }
        }
    }
}

但我收到了The calling thread cannot access this object because a different thread owns it的错误。 我在SO中尝试了类似问题的解决方案,例如添加ConcurrentBag或使用Dispatcher,但它们不起作用。 在Dispatcher的情况下,我无法调用Thread.Sleep,因为它是程序的主线程。

我该如何做到这一点?

3 个答案:

答案 0 :(得分:2)

只是为了完整性添加它,BindingOperations.EnableCollectionSynchronization或多或少地提出你所要求的内容 - 在正确的线程上发送对集合的修改。
如果你想继续使用它,我已成功使用它多次(并且在4.5之前手动完成);只是花时间去理解正在发生的一切,这绝对有帮助:)

答案 1 :(得分:0)

使用DispatcherTimer,它将在正确的线程

上消失

您不需要使用ConcurrentBag。看起来它应该是一个ObservableCollection

答案 2 :(得分:0)

DispatcherTimer是个好主意。我会编写类似于此的处理程序:

// remove outdated
var dtThreshold = DateTime.Now.AddSeconds(-3);
foreach (var el in _localObservable.Where(i => i.DateMessage < dtThreshold).ToList())
    _localObservable.Remove(el);

// add new
ViewMessage message = null;
while (MessageList.TryTake(out message))    
    _localObservable.Add(message);

只能从UI线程处理observable集合 - 因此需要保留ConcurrentBag(从非UI线程添加元素)。

很少有事情需要注意:

    删除部分中的
  • .ToList():这确保在实际删除第一个候选项之前确定所有要删除的候选项(在这种情况下,_localObservable的枚举在修改之前结束)< / p>

  • 无需提前TryPeek - TryTake将带一个元素并将其从包中取出,为您提供元素。

如果您不使用ConcurrentBag,则必须将每个Add操作发布到UI线程。大量的Add事件可能污染UI线程,使一切变得缓慢和放大低效的。