WPF ListView very poor performance with large data - c#

I am experiencing very poor performance with a ListView in WPF, using around 30000 records. As far as I know Virtualisation should be turned on as this is the default (I even turned it on explicitly in the XAML).
The poor performance manifests in this way:
Very slow (a couple of minutes) to do the initial bind
Very slow (over a minute) scrolling
Very slow (again, well over a minute) when you select a row.
I was hoping someone would take a look at the XAML and let me have some thoughts.
<ListView Name="grdComms" Grid.Row="0" Grid.Column="0" SelectedItem="{Binding SelectedHeader}"
VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling"
ScrollViewer.IsDeferredScrollingEnabled="True">
<ListView.View>
<GridView >
<GridViewColumn Header="Account Name" DisplayMemberBinding="{Binding Path=AccountName}" Width="150" />
<GridViewColumn Header="Account Number" DisplayMemberBinding="{Binding Path=AccountNumber}" Width="120" />
<GridViewColumn Header="Type" DisplayMemberBinding="{Binding Path=Type}" Width="80" />
<GridViewColumn Header="Delivery" DisplayMemberBinding="{Binding Path=Delivery}" Width="80" />
<GridViewColumn Header="Count" DisplayMemberBinding="{Binding Path=RequestCount}" Width="80" />
<GridViewColumn Width="80" Header="DeDupe">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Width="80">
<CheckBox HorizontalAlignment="Center" IsChecked="{Binding Path=SelectedForProcessing, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Note: the ItemsSource is set in code, to an ObservableCollection. This is a collection of pretty plain properties (couple of strings, a bool), which is a ViewModel onto the Model, which is (again) strings and bools.
I'm reading where people are using large record sets with no problems, but the various things I have tried don't seem to work.
Any more information required please let me know.

Please ignore me. The problem entirely disappears as soon as I set the MaxHeight of the ListView to something bigger than it needs. I would swear blind I tried this, obviously not.
Move along, there is nothing to see here ...
Gray

Related

Slow performance of list view in WPF

I have a list view with a ton of data and any time I go to scroll / click somewhere on the screen it always takes a few seconds to react. I tried setting the list view to use virtualization but can't seem to get it to work.
I have tried this and this. Not sure if I am doing something wrong or not understanding how the virtualization works?
I do have a lot of styling / added grid view behaviors for my listview. If you have any suggestions please let me know!
What I Have Tried
I have tried removing the CollapseableCollumnBehavior from the list view. Had no change.
Removed all my font styles. Had no change.
Reduced the number of columns. Had a small change but still very unresponsive.
Turned off IsSyncronizedWithCurrentItem. Had no change.
Item Source
My item source is just an observable collection.This is how I load data into it.
private void LoadAllData()
{
using (var uow = _unitOfWorkFactory.Create())
{
foreach (var rule in _ruleRepository.GetAllRulesInCheckProcess())
{
AllRulesInCheckProcess.Add(rule);
}
}
}
XAML:
I took out a lot of my styling and added column behaviors from the xaml to simplify the code.
<ListView SelectedValue="{Binding SelectedRule}"
IsSynchronizedWithCurrentItem="True"
Grid.Column="0"
MinHeight="150"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
ScrollViewer.IsDeferredScrollingEnabled="True"
ItemsSource="{Binding AllRulesInCheckProcess}"
MaxHeight="300"
ScrollViewer.HorizontalScrollBarVisibility="Visible">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Description}" Width="auto">
<GridViewColumnHeader Content="Description">
<GridViewColumnHeader.ContentTemplate>
<DataTemplate>
<StackPanel>
<TextBox Height="25"
FontSize="{StaticResource FontSizeSmall}"
Text="{Binding ElementName=RulesInCheckProgressPage, Path=DataContext.DescriptionFilter}"
Tag="Filter Description"/>
<TextBlock Text="Description"/>
</StackPanel>
</DataTemplate>
</GridViewColumnHeader.ContentTemplate>
</GridViewColumnHeader>
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding LaunchDate,StringFormat=MM/dd/yy}"
Width="auto">
<GridViewColumnHeader Content="Launch Date">
<GridViewColumnHeader.ContentTemplate>
<DataTemplate>
<StackPanel>
<TextBox Height="25"
FontSize="{StaticResource FontSizeSmall}"
Tag="Filter Launch Date"
Text="{Binding ElementName=RulesInCheckProgressPage, Path=DataContext.LaunchDateFilter}"/>
<TextBlock Text="Launch Date"/>
</StackPanel>
</DataTemplate>
</GridViewColumnHeader.ContentTemplate>
</GridViewColumnHeader>
</GridViewColumn>
<GridViewColumn DisplayMemberBinding="{Binding AddedDate,StringFormat=MM/dd/yy}" Width="auto">
<GridViewColumnHeader Content="Added Date">
<GridViewColumnHeader.ContentTemplate>
<DataTemplate>
<StackPanel>
<TextBox Height="25"
FontSize="{StaticResource FontSizeSmall}" Tag="Filter Added Date"
Text="{Binding ElementName=RulesInCheckProgressPage, Path=DataContext.AddedDateFilter}"/>
<TextBlock Text="Added Date"/>
</StackPanel>
</DataTemplate>
</GridViewColumnHeader.ContentTemplate>
</GridViewColumnHeader>
</GridViewColumn>
</GridView>
</ListView.View>
UPDATE
So through doing some more research it appears I may need to take a look at data virtualization instead of UI virtualization. Does anyone know of any up to date data virtualization code?

Is there a way to produce a scrollable gridlayout with dynamic content?

Ì want to write a software where there is a grid layout inside a scrollview.
I already hardcoded it, but I think I need to find a solution to make this dynamic! How can I manage to do this! I am pretty new to C# and WPF. I post a screenshot and my code so you can see what I am trying to achieve.
DataGrid is probably the best thing to use here, although you can also do it with a ListView using a GridView as the view:
<ListView ItemsSource="{Binding Items}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Age" DisplayMemberBinding="{Binding Age}" />
<GridViewColumn Header="Gender" DisplayMemberBinding="{Binding Gender}" />
</GridView>
</ListView.View>
</ListView>

Listview DataTemplate Binding returning an error

I'm trying to switch over to WPF from Winform and so far it's a pain.
Anyway, I am trying to get this binding thing to work using DataTemplate.
I have a class:
public class TranslatorListItem
{
public string Item { get; set; }
public string OriginalMessage { get; set; }
public string TranslatedMessage { get; set; }
public string Sector { get; set; }
}
Items are added like this:
TranslatorListItem TLI = new TranslatorListItem();
TranslatorLVI.Items.Add(TLI);
My XAML DataTemplate:
<DataTemplate x:Key="MyDataTemplate">
<Border BorderBrush="#FFA4D5E5" BorderThickness="1,1,0,0" Margin="6">
<StackPanel Margin="6,2,6,2">
<TextBox Text="{Binding}" TextWrapping="Wrap" BorderThickness="0" BorderBrush="#00000000" />
</StackPanel>
</Border>
</DataTemplate>
This is how I'm trying to bind the data but it's returning this error: "Two-way binding requires Path or XPath."
<ListView Margin="23,224,27,54" Name="TranslatorLVI" ItemsSource="{Binding}" HorizontalContentAlignment="Stretch"
ItemContainerStyle="{StaticResource MyItemContainerStyle}">
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn Header="Item" DisplayMemberBinding="{Binding Path=Item}" CellTemplate="{StaticResource MyDataTemplate}" />
<GridViewColumn Header="Original Message" Width="300" CellTemplate="{StaticResource MyDataTemplate}" />
<GridViewColumn Header="Translated Message" DisplayMemberBinding="{Binding Path=TranslatedMessage}" CellTemplate="{StaticResource MyDataTemplate}" />
<GridViewColumn Header="Sector" DisplayMemberBinding="{Binding Path=Sector}" />
</GridView>
</ListView.View>
</ListView>
I need TranslatedMessage binding to be editable. So that field wont be Read-Only.
I heard I may need to set Two-way binding, but I am not sure how to do so.
Any help is appreciated.
Thanks!
I'm trying to switch over to WPF from Winform and so far it's a pain.
No, it's not. WPF is the best UI Framework in the history of mankind. winforms (or anything else) can't even be compared with it.
Your problem is you're trying to use a winforms approach in WPF and you fail.
WPF does not support developers with a winforms mentality.
All the horrible code behind hacks you're used to from winforms are completely unneeded in WPF.
Please read Rachel's Excellent Answer (and linked blog posts) about the mindshift needed when upgrading from winforms to WPF.
As mentioned in #JasRaj's answer, you're missing a DisplayMemberBinding in this case. I tested your code and added that, like this:
<GridViewColumn Header="Original Message"
DisplayMemberBinding="{Binding OriginalMessage}"
Width="300"/>
And it worked perfectly.
After a second read to your question I realized you wanted to make one of these columns editable.
First of all I will say that while what you want can be achieved with a ListView, using a DataGrid is easier.
You need to remove the DisplayMemberBindings from the ListView in order to use a CellTemplate, and therefore you need to modify the template slightly:
<DataTemplate x:Key="MyDataTemplate">
<Border BorderBrush="#FFA4D5E5" BorderThickness="1,1,0,0" Margin="6">
<TextBox Text="{Binding TranslatedMessage}"
TextWrapping="Wrap"
BorderThickness="0"
BorderBrush="#00000000" />
</Border>
</DataTemplate>
I have placed a ListView and a DataGrid side by side so you can compare them:
Here is the XAML:
<UniformGrid Columns="2">
<ListView ItemsSource="{Binding}" HorizontalContentAlignment="Stretch">
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn Header="Item" DisplayMemberBinding="{Binding Path=Item}"/>
<GridViewColumn Header="Original Message" DisplayMemberBinding="{Binding OriginalMessage}" Width="300"/>
<GridViewColumn Header="Translated Message" CellTemplate="{StaticResource MyDataTemplate}" />
<GridViewColumn Header="Sector"/>
</GridView>
</ListView.View>
</ListView>
<DataGrid ItemsSource="{Binding}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Item" Binding="{Binding Path=Item}" IsReadOnly="True"/>
<DataGridTextColumn Header="Original Message" Binding="{Binding OriginalMessage}" IsReadOnly="True" Width="300"/>
<DataGridTextColumn Header="Translated Message" Binding="{Binding TranslatedMessage}" />
<DataGridTextColumn Header="Sector" Binding="{Binding Sector}"/>
</DataGrid.Columns>
</DataGrid>
</UniformGrid>
Notice how the ListView requires DataTemplates in order to support edition, and is read-only by default, while the DataGrid is editable by default, does not need any special templats and requires that you add the IsReadOnly="True" to columns that are intended to be read-only.
Also, I noticed you're adding items manually to the ListView using procedural code. This is not desired in WPF. You must use an ObservableCollection<> and manipulate that collection, and let the WPF Binding engine update the UI for you.
It is recommended and desired that you Separate logic/data from UI in WPF.
If you need further clarification please let me know.
WPF Rocks.
Bind the second columns of Gridview with OriginalMessage property.
like :-
DisplayMemberBinding="{Binding Path=OriginalMessage}"
it should work.
In my opinion you dont fully use the datatemplate. you just use it to display one property;
In fact you can use a DataTemplate to display all data in thie class (TranslatorListItem);
such as this
<DataTemplate x:Key="MyDataTemplate">
<Border BorderBrush="#FFA4D5E5" BorderThickness="1,1,0,0" Margin="6">
<TextBox Text="{Binding Item}"
TextWrapping="Wrap"
BorderThickness="0"
BorderBrush="#00000000" />
<TextBox Text="{Binding TranslatedMessage}"
TextWrapping="Wrap"
BorderThickness="0"
BorderBrush="#00000000" />
( ..... follow up)
</Border>
</DataTemplate>
So your ListView can design like this:
<ListView ItemsSource="{Binding}" HorizontalContentAlignment="Stretch"
ItemTemplate="{StaticResource MyDataTemplate}">
</ListView>

Control suggestions that function like a multi column listBox

I've looked at a bunch of different controls(ListView, GridView, etc.) and can't decide which makes the most sense for me to use.
I want something that looks and functions just like a listBox with the ability to select a row with a single click, except it would contain data with multiple columns.
I'm just looking for suggestions on which control to use and how I would go about having it select a row(whether it's a selectionMode or an onClick function or what). Since I'm new to these controls I'd like some direction on which tag to place those selection options, I think I can figure out the rest however.
Thanks :)
I like using the ListView that contains a GridView as it's view. Here's how I defined it in XAML:
<ListView Name="lstCurrentInvoices" Grid.Row="4" Margin="0,0,0,0" SelectionMode="Extended" ToolTip="Invoices included in invoice file." IsTabStop="True" TabIndex="8">
<ListView.View>
<GridView>
<GridViewColumn Width="40" Header="ID" DisplayMemberBinding="{Binding ClientId}"/>
<GridViewColumn Width="170" Header="Name" DisplayMemberBinding="{Binding ClientName}"/>
<GridViewColumn Width="80" Header="Date" DisplayMemberBinding="{Binding InvoiceDate}"/>
<GridViewColumn Width="40" Header="Frequency" DisplayMemberBinding="{Binding Frequency}" />
</GridView>
</ListView.View>
</ListView>
This way you kind of get the best of both worlds. In this example, you can have multiple rows selected. You can detect which rows have been selected and grab the objects from you data source. It's really quite powerful. Hope this helps

ListView that imitates Windows Explorer

How can I make a ListView imitate the ListView in Windows Explorer on the right side. Like how can I get icons in the ListView and get the arrows?
You can find the icons by using Google Image search. To create the ListView, you could do something like this is XAML:
<Grid>
<ListView ItemsSource="{Binding ListViewSource}">
<ListView.View>
<GridView>
<GridViewColumn Width="25">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Icon}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding FileName}" Width="250"/>
<GridViewColumn Header="Date Modified" DisplayMemberBinding="{Binding DateModified}" Width="100"/>
<GridViewColumn Header="Type" DisplayMemberBinding="{Binding FileType}" Width="100"/>
<GridViewColumn Header="Size" DisplayMemberBinding="{Binding FileSize}" Width="100"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
The next step is to create an ObservableCollection to hold all of the items in your list and call it ListViewSource. You can populate this collection with actual FileDirectory information, or your own kind of list. You'll then want to create your logic as to what happens when you doubleclick on an item. Since your question didn't specify to what detail you want the ListView to work, I'm going to stop there. Let us know if you want it to behave just like Windows Explorer, and we'll try to help you out.

Categories