我有一个带有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
,因为它是程序的主线程。
我该如何做到这一点?
答案 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线程,使一切变得缓慢和放大低效的。