views:

86

answers:

5

Hi. I need to remove items from list few seconds after i added them. I have now an ObservableCollection to which I add some messages. I need them to be deleted let's say 5 seconds after they were added. I tried to create a function responsible for adding the items and setting a timer:

public void AddInfoItem(string info)
    {
        infoList.Add(info);
        Timer newTimer = new Timer(5000);
        newTimer.Elapsed += new ElapsedEventHandler(this.TimerFunction);
        newTimer.Enabled = true;
        newTimer.Start();
    }
public void TimerFunction(Object sender, EventArgs e)
    {
        infoList.Clear();
    }

I didn't even send any parameters which item should be removed cause the second function raised an exception. Can somebody describe a proper solution to add item and delete it after some time?

Sory for not writing it earlier. The exception is

this type of collectionview does not support changes to its sourcecollection from a thread different from the dispatecher thread

A: 

make sure that your garbage collector didn't dispose of your infoList. Try GC.KeepAlive(infoList)

SideFX
Seems unlikely to be the cause... Since infoList is defined (and thus referenced) somewhere outside of the method, it seems unlikely that GC is going to be the cause. You'd have to set `infoList = null` for GC to clean it up, and that'd give a `NullReferenceException` (which hopefully wouldn't need a SO question to resolve...).
Dan Puzey
GC.KeepAlive does nothing. You can implement it like this `void Foo(object bar){}`. It is only ever useful when you don't have any references to an object that is used somewhere outside the reach of the garbage collector, like unmanaged code.
Martinho Fernandes
+1  A: 

If working in WPF use DispatcherTimer. I usually use something like this:

public static void Delay(int milliseconds, Action action)
{
    var t = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(milliseconds) };
    t.Tick += (o, e) => { t.Stop(); action.Invoke(); };
    t.Start();
}

DispatcherTimer will make sure the event is invoked on the same thread so you don't run into threading issues. Another alternative is to make the collection you're binding to thread safe. But indeed, knowing what kind of exception you received instead of guessing would be easier.

Also, if you add and remove lots of items in quick succession or require your timing to be precise you'd need to consider something else; DispatcherTimer is not very precise and does carry some overhead, so a lot of instances of it will consume some resources.

Alex Paven
A: 

Sounds like a job for Reactive Extensions. Here's a link to 101 Rx Samples to show you how to get going.

Basically what you'll want to do (partly pseudo code)

Observable
.FromEvent<NotifyCollectionChangedEventArgs>(infoList, "CollectionChanged")
.Where(args => args.Action == NotifyCollectionChangedAction.Add)
.Delay(TimeSpan.FromSeconds(5))
.ObserveOnDispatcher()
.Subscribe(args => infoList.Remove(args.NewItems));

No need to deal with the timers and no leaks as long as you dispose of the IDisposable returned by the Subscribe method when you're done with it.

Edit: Shameless self plug - I did a blog post with a working console app example. The one difference you'll have is you'll want to keep the ObserverOnDispatcher() call in your WPF app or you'll get some threading errors.

Bryan Anderson
A: 

Piling onto Bryan's answer above, here's how you do it via ReactiveXaml's ReactiveCollection, an extension of ObservableCollection:

theCollection.ItemsAdded
    .Delay(TimeSpan.FromSeconds(5))
    .Subscribe(theCollection.Remove);
Paul Betts
That looks like a really cool library! I'm definitely adding it to my list of projects to keep watch.
Bryan Anderson
+1  A: 

Rather removing items from the list on a timer, why not store an expiration time with each item when it is added, and ignore or remove expired items only when you need to retrieve an item or iterate the list?

kindall