WinterS
WinterS

Reputation: 147

ObservableCollection triggering too many CollectionChanged

I have this code below which perform async update of an ObservableCollection of Messages which are collected over an Ethernet Connection

ObservableCollection

private readonly ObservableCollection<MsgDisplayViewModel> _rxGridMessages;
public ObservableCollection<MsgDisplayViewModel> RxGridMessages
{
    get { return _rxGridMessages; }
}

Action

_rxMsgUpdater = new Action<RxMessage>((RxMessage msg) =>
    {
        if (_rxGridMessages.Count < 2000)
        {
            _rxGridMessages.Add(new MsgDisplayViewModel(msg, DataCollection.DataBase));
        }
        else
        {
            var dispmsg = new MsgDisplayViewModel(msg, DataCollection.DataBase);
            _rxGridMessages[index++ % _rxGridMessages.Count] = dispmsg;
        }
    }
);

BeginInvoke called by Background thread from RxEventHandler

    private void RxEventHandler(RxMessage msg)
    {
        UiDispatcher.BeginInvoke(_rxMsgUpdater, DispatcherPriority.Normal, msg);
    }

XAML

<DataGrid Name="TraceGrid" 
     ItemsSource="{Binding MyVm.RxGridMessages, Mode=OneWay, IsAsync=True}"
     SelectionMode="Single"  AutoGenerateColumns="False" VirtualizingPanel.IsVirtualizing="True" FontSize="10" 
     HorizontalAlignment="Stretch"  IsReadOnly="True" RowDetailsVisibilityMode="Visible" BorderBrush="{DynamicResource AccentColorBrush}" BorderThickness="1"   EnableColumnVirtualization="True"
          EnableRowVirtualization="True" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.IsDeferredScrollingEnabled="True"                        
          >
    <DataGrid.Columns>

...

The UI is fine and good as long as the background thread is calling the RxEventHandler something like 20 times per second. Once I put the real traffic in, the RxEventHandler is called something like 1000 times per second.

So I tried something by extending the ObservableCollection, I started collecting the changed items on the base OnCollectionChanged of ObservableCollection and trigger every 500 ms a CollectionChanged event. It obviously threw RangeNotSupportedException and I had to use the following instead,

    try
    {
        base.OnCollectionChanged(new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Add, addedItems, itemaddedindex));
    }
    catch (System.NotSupportedException)
    {
        base.OnCollectionChanged(new System.Collections.Specialized.NotifyCollectionChangedEventArgs(System.Collections.Specialized.NotifyCollectionChangedAction.Reset));
    }

The problem is that the collection is rebuild and there is some delay before it gets displayed. If Collection item count is low it is pretty quick. But when it is 1000 or so then it takes some seconds.

I am feeding this to the datagrid as shown above and I have enabled Virtualization as well. Now I have done a lot of googling and I have not found someone with a work around for this problem. Can anybody advice me on this?

I have done some profiling and the CPU hog is PresentationFramework.ni.dll and it seems related to the numerous CollectionChanged events. Right now I have something responsive but at the cost of limiting the Collection count. This is not what I wanted to do.

Upvotes: 0

Views: 789

Answers (2)

WinterS
WinterS

Reputation: 147

new in Visual Studio 2015

BulkObservableCollection

https://msdn.microsoft.com/en-us/library/dd867973.aspx

Upvotes: 1

user3094087
user3094087

Reputation:

Due to the nature of this problem, you should use some type of processing queue and then create a handler that handles the processing sepeerately. Furthermore because of the sheer amount of data you are working with, you should either use a timer to query for new updates every x seconds/minutes and refresh datagrid that way , or you should use some type of virtualizing panel that can handle the sheer amount of data being passed into it(ListView/GridView)

Upvotes: 3

Related Questions