Slow switch between view (views contains datagrid with many items)

Aug 9, 2011 at 7:26 PM
Edited Aug 9, 2011 at 7:51 PM

Hi,

I use Caliburn Micro & Fluent Ribbon in my WPF app based on .NET 4.0. My problem is that change between views is too slow. I think that I know root of this problem.

But I don’t how to solve. So first I describe design of my app.

Shell is WPF window and it contains Fluent Ribbon.Menu with RibbonTabItem items. If user click on tab item in shell is loaded new view.

Here is XAML of view:

 

<Fluent:RibbonWindow x:Class="T_TOOL.Views.ShellView" >

        <StackPanel Grid.Row="0">
            <Fluent:Ribbon>
                <Fluent:Ribbon.Menu>

                    <Fluent:Backstage>
                        <Fluent:BackstageTabControl>
                            <Fluent:BackstageTabItem Header="Open"/>
                            <Fluent:BackstageTabItem Header="Close"/>
                        </Fluent:BackstageTabControl>
                    </Fluent:Backstage>

                </Fluent:Ribbon.Menu>

                <!--Tabs-->
                <Fluent:RibbonTabItem Micro:Message.Attach="[PreviewMouseLeftButtonDown]=[Action ShowView1()]"/>
		<Fluent:RibbonTabItem Micro:Message.Attach="[PreviewMouseLeftButtonDown]=[Action ShowView2()]"/>
		<Fluent:RibbonTabItem Micro:Message.Attach="[PreviewMouseLeftButtonDown]=[Action ShowViewN()]"/>

            </Fluent:Ribbon>

        </StackPanel>

        <Grid Grid.Row="1">
            <ContentControl x:Name="ActiveItem" />
        </Grid>
    </Grid>
</Fluent:RibbonWindow> 

 

ShellView model class:

 

 

namespace T_TOOL.ViewModels
{

    public interface IShellViewModel{}

    [Export(typeof(IShellViewModel))]
    public class ShellViewModel :Conductor<IScreen>.Collection.OneActive,
        IShellViewModel, 
        IPartImportsSatisfiedNotification
    {

        public void ShowView1()
        {
            var screen = IoC.Get<IShowView1();
            ActivateItem(screen);
        }

        public void ShowView2()
        {
            var screen = IoC.Get<IShowView2();
            ActivateItem(screen);
        }

//...
        public void ShowViewN()
        {
            var screen = IoC.Get<IShowViewN();
            ActivateItem(screen);
        }

    }
}

 

ViewModel1, ViewModel2, ...ViewModelN contains only DataGrid controls. On Datagrid control I bind property type of ICollectionView from view model.

This property contains 18 000  -  25 000 items. I think this is root of problem why is change between Views is slow. I use DataGrid Control from Extended WPF Toolkit.

I set EnableRowVirtualization and EnableColumnVirtualization properties of DataGrid on true. But it doesn’t help.

 

Here is View XAML code of ViewModel1:

 

        <Style x:Key="MainView_CallsDataGrid" TargetType="{x:Type Controls:DataGrid}" 
               BasedOn="{StaticResource MainView_FontBaseStyle}">
            <Setter Property="AutoGenerateColumns" Value="False"/>
            <Setter Property="VerticalScrollBarVisibility" Value="Visible"/>
            <Setter Property="HorizontalScrollBarVisibility" Value="Visible"/>
            <Setter Property="VerticalAlignment" Value="Stretch"/>
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="Margin" Value="4,15,4,15"/>
            <Setter Property="EnableRowVirtualization" Value="True"/>
            <Setter Property= "EnableColumnVirtualization" Value="True"/>
        </Style>



 <Controls:DataGrid   ItemsSource="{Binding CallsView, Mode=OneWay,
						UpdateSourceTrigger=PropertyChanged}">


            <Controls:DataGrid.Columns>

                <Controls:DataGridTextColumn IsReadOnly="True"
                                             CellStyle="{StaticResource MainView_CallsDataGrid_CellStyle}"
                                             Binding="{Binding Path=Number}"
                                             Header="Cell phone No"/>

                <Controls:DataGridTextColumn IsReadOnly="True"
                                             CellStyle="{StaticResource MainView_CallsDataGrid_CellStyle}"
                                             Binding="{Binding Path=CallType}"
                                             Header="Call type"/>

                <Controls:DataGridTextColumn IsReadOnly="True"
                                             CellStyle="{StaticResource MainView_CallsDataGrid_CellStyle}"
                                             Binding="{Binding Path=Dt}"
                                             Header="Date / Time"/>


                <Controls:DataGridTextColumn IsReadOnly="True"
                                             CellStyle="{StaticResource MainView_CallsDataGrid_CellStyle}"
                                             Binding="{Binding Path=CallingNumber}"
                                             Header="Calling Number"/>


                <Controls:DataGridTextColumn IsReadOnly="True"
                                             CellStyle="{StaticResource MainView_CallsDataGrid_CellStyle}"
                                             Binding="{Binding Path=VoiceNetwork}"
                                             Header="Voice network"/>

                <Controls:DataGridTextColumn IsReadOnly="True"
                                             CellStyle="{StaticResource MainView_CallsDataGrid_CellStyle}"
                                             Binding="{Binding Path=Type}"
                                             Header="Type"/>

                <Controls:DataGridTextColumn IsReadOnly="True"
                                             CellStyle="{StaticResource MainView_CallsDataGrid_CellStyle}"
                                             Binding="{Binding Path=TalkTime}"
                                             Header="Talk time"/>

                <Controls:DataGridTextColumn IsReadOnly="True"
                                             CellStyle="{StaticResource MainView_CallsDataGrid_CellStyle}"
                                             Binding="{Binding Path=Price}"
                                             Header="Price"/>

            </Controls:DataGrid.Columns>

</Controls:DataGrid>

 

code from ViewModel1 class:

 

        public ICollectionView CallsView
        {
            get
            {
                return _callsView;
            }
            set
            {
                _callsView = value;
                NotifyOfPropertyChange(() => CallsView);
            }
        }

//... Init CallsView property from List<T> property

       CallsView = CollectionViewSource.GetDefaultView(List<T>);
       FilterCalls();
       CallsView.Refresh();

//Filter method
        private void FilterCalls()
        {
            if (CallsView != null)
            {
                CallsView.Filter = new Predicate<object>(FilterOut);
                CallsView.Refresh();
            }
        }

 

 

My opinion is correct? Switch betweens views is slow because datagrid contains  many rows? Or problem is that I bind on DataGrid control property type of ICollectionView?

Thank you for your opinions, suggestions and feedback.

 

Here is screen-shoot.

 

Aug 9, 2011 at 8:29 PM

If you take the grid out of the View is it still slow? If it is I would suggest looking at other methods of loading the grid. That would be better dealt with on StackOverflow.com

Aug 9, 2011 at 8:47 PM

I'm sorry to say that I'm lately having an annoying performance issue with DataGrid myself... and I'm (still) losing.
Virtualization mechanism seems to be affected by a lot of factors, mainly related to a particular layout (for example a stackpanel o scrollpanel around the grid) or condition forcing the grid to populate cells for the whole collection.
The number of rows obviously affect performances, but I happened to work with quite large set of data, in past projects, with fairly good result.
Also, I believe that ICollectionView isn't adding any overhead, since DataGrid uses it internally anyway.

I'll keep you posted on my progress, should I get any improvement in my scenario. 

 

Aug 9, 2011 at 10:27 PM

May I ask you the reason you are using an ICollectionView directly? Is it just to support filtering? Why not using a CollectionViewSource instead?

Apart from this, have you verified if the DataGrid is truly virtualizing? First of all, I would use Snoop to check what kind of Panels are used to display the columns and the rows, then I would create a dummy control inside the cell template, to track construction and finalization, just to verify if virtualization is taking place.

As Marco says, there are a lot of situations where virtualizing controls cannot achieve virtualization due to some circumstances (grouping is one of these, but it is not the only one). Again, I would first use a CollectionViewSource to be bound to the datagrid, then check if virtualization is taking place.

Aug 10, 2011 at 8:54 AM
.BladeWise wrote:

May I ask you the reason you are using an ICollectionView directly? Is it just to support filtering? Why not using a CollectionViewSource instead?

Apart from this, have you verified if the DataGrid is truly virtualizing? First of all, I would use Snoop to check what kind of Panels are used to display the columns and the rows, then I would create a dummy control inside the cell template, to track construction and finalization, just to verify if virtualization is taking place.

As Marco says, there are a lot of situations where virtualizing controls cannot achieve virtualization due to some circumstances (grouping is one of these, but it is not the only one). Again, I would first use a CollectionViewSource to be bound to the datagrid, then check if virtualization is taking place.

Hi, thank for response. What is useful for change

        public ICollectionView CallsView
        {
            get
            {
                return _callsView;
            }
            set
            {
                _callsView = value;
                NotifyOfPropertyChange(() => CallsView);
            }
        }

for

        public CollectionViewSorce CallsView
        {
            get
            {
                return _callsView;
            }
            set
            {
                _callsView = value;
                NotifyOfPropertyChange(() => CallsView);
            }
        }

//IList<Call>calls
CallsView=new CollectionViewSource{Source = calls};

because if I use this solution, datagrid is empty... maybe I dont understand to you....

Aug 10, 2011 at 12:43 PM

As far as I know, the CollectionViewSource is more or less the view-model of a CollectionView, and tipically you should use it to manipulate filtering and sorting for databinding.

You can get more information in this post from Bea Stollniz.

I fear that the way you are using the CollectionViewSource is not correct, repsecting the way it works. Note that in the post I cited above, Bea says that the binding engine will automatically perform a binding over CollectionViewSource.View (unless you specify BindsDirectlyToSource, which I suppose is not correct in this case), so in case you are swapping the CollectionViewSource, the binding will not be re-evaluated. Try to use a single CollectionViewSource, and swap the CollectionViewSource.Source property instead.