xaml code
<ListView Name="lvw" VirtualizingStackPanel.IsVirtualizing="True" ItemsSource="{Binding Source={StaticResource MyList}}" >
<ListView.View>
<GridView AllowsColumnReorder="true" VirtualizingStackPanel.IsVirtualizing="True" >
<GridViewColumn x:Name="MiaCode" DisplayMemberBinding="{Binding Path=MIACODE}" Header="code" Width="80" />
<GridViewColumn x:Name="MiaName" DisplayMemberBinding="{Binding Path=MIANAME}" Header="name" Width="270"/>
</GridView>
</ListView.View>
</ListView>
//Binding Data count is over 10000
even though I set the property [VirtualizingStackPanel.IsVirtualizing="True"],
it takes too long to display data.
is there anything wrong in my code??
Is MyList a static data? you can use asynchronous binding by setting IsAsync property Binding.IsAsync Property or asynchronous data loading see ObjectDataProvider.IsAsynchronous Property,or develop some paging mechanism VirtualizingStackPanel.IsVirtualizing = "true" is not speeding up data loading time, it just doesn't create UI elements that are not visible Hope this helps
Confirming the correctness of arsenmkrt's reply.
A virtualized items source is generally pretty fast as long as it's not waiting for the underlying data to load.
If you get no joy, here are some more considerations:
Try pre-loading/caching your data if possible.
Double check that the property getters for MIACODE and MIANAME aren't performing CPU instensive work, and aren't creating any side effects.
Use Linq to page the in-bound data.
From experience, though, I can confirm that the GridView on a virtualized listview is amply capable of displaying thousands of items without severe performance panalties. Hope this helps you.
Oh... just spotted that you're binding to a static resource....
If you are creating an explicit CollectionViewSource resource for grouping and sorting your items, take care to note that the by-property sorting mechanism of the CollectionView is known to be slow.
If this scenario describes what you're doing, try using a customer sort filter with the collection view (which is considerably faster), or better yet retrieve your data pre-sorted the way it needs to be.
Broadly speaking, you can only rely on the based sorting of CollectionView up to a few thousand items before the sorting time shows it's poorer performance.
Related
I am currently building an UWP application which should control several jobs, where each job could be updated from a background thread. Currently, I am using an ObservableCollection in a static "Core-Class" in which all jobs are stored (see question 1). This list is bound using a property in the view model of my view:
public ObservableCollection<JobBase> Jobs
{
get
{
return Core.Jobs;
}
set
{
Core.Jobs = value;
}
}
The list view's ItemsSource is bound to this property in my view model. I am using a custom ListView.ItemTemplate which uses a DataTemplate linked to my Job-class to display the information for each job:
<ListView
x:Name="JobsListView"
Grid.Row="1"
ItemsSource="{x:Bind ViewModel.Jobs, Mode=OneWay}"
SelectionMode="Single"
IsItemClickEnabled="True"
SelectedItem="{x:Bind ViewModel.RunningJob, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="lib:JobBase">
<Grid Margin="0,12,0,12" Height="52">
[...]
<TextBlock FontSize="12" Text="{x:Bind Progress, Mode=OneWay}" Margin="4,0,0,0" />
[...]
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Some thoughts about this construct:
1) I think using an ObservableCollection in my static "background" Core-Class is not beautiful. I could change this to a normal List and do a cast to an ObservableCollection in the property of my list view. However, I will need some events when new jobs are added (or old ones deleted) in order to update my ObservableCollection which is bound the to the ListView. Is this the preferred implementation?
2) As soon as the progress of a certain job is updated (from within a background thread), I cannot show those updates in the list view. Even if I implement events for this, I have no clue how to update the binding of a certain item within my list view?
3) I could implement the INotifyPropertyChanged interface in my Job class, but I think this would not be a beautiful implementation either. In addition, I am raising exceptions as I cannot update my GUI from a background thread?
As you see, I am looking for a "professional" implementation on this issue. Therefore, I would prefer a solution which uses bindings and avoids a complete reloading of the whole job list if only a single item is updated.
I am really looking forward for your implementation tips :-)
You should not be replacing the instance of the object being bound to ItemsSource. Instead, you'll need to modify the contents of the list. You can replace the ItemsSource if you choose, but this can lead to serious heap fragmentation and performance problems.
To update the UI thread, use CoreDispatcher.RunAsync to get back on the UI thread from a thread that could potentially be in the background https://learn.microsoft.com/en-us/uwp/api/windows.ui.core.coredispatcher.runasync?view=winrt-18362
await _coreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
this.Bindings.Update();
});
If you need to just update one item at a time, ObservableCollection could be ideal, especially when the List includes objects which implement INotifyPropertyChanged. If instead you prefer to update several objects at once you'd set the binding to OneTime then use Bindings.Update() to update all the bindings in the ListView at once. This documentation can help to provide more info about optimizing your ListView. https://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/optimize-gridview-and-listview
ObservableCollection isn't necessary. There are other mechanisms of data binding if you prefer.
For more on data bindings, see https://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth
I have two DataGrids that have the same number of rows. There is also a hidden integer field/column on each row for maintaining a 1-1 correspondence between the two. When the user sorts, selects, or scrolls, the action on one DataGrid needs to be replicated on the other. I am most concerned about sorting, since I'm afraid the DataGrid's sort operations may interfere or may not reflect my own ordering behind the scenes. What's the best way to maintain these views?
(It is for a surveying program in which one grid has a list of x,y,z coordinates and the second list has the same points with a transformation applied. These are split between two tabs for reasons of screen real-estate.)
On large datasets sorting may be expensive. If screen real estate is the issue you can have one datagrid with one type of items and switch the item template to switch between both views. This way screen estate is retained, sorting isn't done twice and you have a very reliable way of keeping the sort between both views in sync.
there are many ways to achieve the effect, the best approach depends on the specific circumstances.
One approach would be to set two different <GridView>s as resources and select which one is used either in code or through Binding with a custom converter:
<Grid>
<Grid.Resources>
<GridView x:Key="DefaultView">
<GridViewColumn Width="120"....etc/>
</GridView>
<GridView x:Key="AlternativeView">
<GridViewColumn Width="50"....etc/>
</GridView>
</Grid.Resources>
<ListView x:Name="MyListView" ItemsSource="{Binding DisplayValues}" View="{StaticResource DefaultView}"/>
</Grid>
and in code behind you can switch the view, something like this:
MyListView.View = isDefault ? MyListView.TryFindResource("DefaultView") : MyListView.TryFindResource("AlternativeView")
It's also possible to use Binding and skip the code behind.
Another approach would be to make a user control for one of the column items where the user control can display the data in two alternative states.
Yet another possiblity is to use ItemTemplateSelector
I have a control that uses a ParentObject as its DataContext. The ParentObject has a property called ChildObject that may change. When it does, ParentObject raises the PropertyChanged event.
The control has XAML to define a ListView for the items in the List property of the ChildObject. When the ChildObject property changes in the ParentObject, the entire view is recreated, meaning that new controls are instantiated.
The ListView is actually much more complicated than the example below, so recreating it is processor intensive and takes a long time.
What are my other options? Can I cache the entire ListView for each ChildObject? How would I go about doing that?
<ListView ItemsSource="{Binding ParentObject.ChildObject.List}">
<ListView.View>
<GridView>
<GridViewColumn Header="Error">
<GridViewColumn.CellTemplate>
<DataTemplate>
<local:ErrorControl DataContext="{Binding ErrorCollection}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
The listview is creating new controls becuase of your datatemplate. With data templates, you have a one-to-one relationship between the instance of your data and the controls inside of the datatemplate. You have a few options:
1) Have you looked into virtualization? This will only render what is visible. If you have a high number of objects, you definitely want to consider this.
2) Have you thought about re-working your view-model to minimize the change in your collections? Instead of adding/removing objects anytime an error changes. Re-use the objects in the collection and only add/remove when your total count changes.
I've done both of these to slow changes to the view from the view-model. They both improved performance significantly, but I had hundreds of objects on the screen.
I have a WPF C# datagrid that is read only, its contents are loaded from an external XML file and other forms manipulate the XML file by adding, editing and removing data.
I would like the datagrid to reload when a change is made, however there doesn't appear to be an easy way of doing this.
I intend on putting a 'refresh' of some sort when the 'editing' form closes.
I have tried datagrid.items.refresh() without success among a few other bits of failed code. (learning C#/WPF)
How am I supposed to do this?
XAML:
<Grid.DataContext>
<XmlDataProvider Source="E:\downloader\downloadConfig.xml" XPath="/download/downloadItem"></XmlDataProvider>
</Grid.DataContext>
<DataGrid x:Name="downloadList" Height="191" VerticalAlignment="Top" ItemsSource="{Binding}" AutoGenerateColumns="False" AlternatingRowBackground="Gainsboro" IsReadOnly="True" DataContext="{Binding}" IsSynchronizedWithCurrentItem="True">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding XPath=ID}" Width="50"></DataGridTextColumn>
<DataGridTextColumn Header="Name" Binding="{Binding XPath=Name}" Width="350"></DataGridTextColumn>
<DataGridTextColumn Header="Status" Binding="{Binding XPath=Status}" Width="100"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
Bind the DataGrid to a collection that implements the INotifyCollectionChanged interface. Objects that implement this interface will raise events when their contents change, and DataGrid will listen for these events and update itself accordingly.
There is a built-in generic class, ObservableCollection, which takes care of all of this for you. It's usually easiest to just use it. It does have one gotcha, though, which is that it can only be modified from the main thread. If you need to modify it from another thread, use Dispatcher.Invoke (or BeginInvoke) to avoid getting exceptions.
Note that these only notify of 'row-level' changes - the addition, removal, replacement of entire objects from the collection. To have the DataGrid also update itself when objects within the collection change, implement INotifyPropertyChanged on them.
I realize this means a bunch of extra coding since you'll need to implement classes to go between the XML and the collection, but it is the preferred option. On the upside, it should perform better. The DataGrid will be able to only update the rows it needs to update, rather than completely redrawing itself (which can be an expensive operation in WPF).
To date every ListView I've had I just set ItemSource={Binding} in my Xaml and then in the .CS file I say listview.datacontext = myobject and the view loads just fine. But now I need to have a list that updates as the data updates as well. So after some research I discovered ObservableCollections and rewrote my code to use that. But I can't get my data to display when setting the listview to my dataobject.
My Xaml:
<ListView ItemsSource="{Binding Tests}" Name="DataCompareTests" Margin="0,0,5,0" Grid.Column="0">
<ListView.View>
<GridView>
<GridViewColumn Header="TestCase" Width="200" DisplayMemberBinding="{Binding name}" />
</GridView>
</ListView.View>
</ListView>
My Xaml.cs:
readonly DataCompare dataCompare = new DataCompare();
public void Execute_Click(object sender, RoutedEventArgs e)
{
var Tests = new ObservableCollection<TestCases>();
Tests = dataCompare.LoadTestCases(); //located in another class file
//DataCompareTests.DataContext = Tests;
}
If I remove the "Tests" part of the binding in my Xaml and remove the comments from the .DataContext line above, the view displays the correct information. However it's my assumption that if I want my view to update as the data does I need to specify my object in the binding. How do I properly set that? I can't seem to find the correct answer.
Thanks,
Jason
I think you need to familiarize yourself a little better with bindings and object oriented programming in general.
If you set your datacontext to your model object, ".Tests" should be a public property of that model object. Also, don't do this:
var someVariable = new SomeClassThatTakesWorkToConstruct();
someVarialbe = someOtherVariable.SomeMethod();
What you meant to do was this:
var someVariable = someOtherVariable.SomeMethod();
This is for 2 good reasons 1) You are not wasting the construction of an ObservableCollection. 2) Your code will be easier to refactor (the type returned by SomeMethod can change without you having to alter your declaration of someVariable).
Edit, additional resources:
Databinding Overview
You've got a path specified but no source for the binding specified.
MVVM Article
Great article on using the common MVVM WPF pattern, helps you keep your code object oriented, clean, etc. even with complex UI interaction.
It would appear my concerns were pointless and I WAS doing this the proper way in the first place.
According to MSDN:
"However, if you are binding to an object that has already been created, you need to set > the DataContext in code, as in the following example.
...
myListBox.DataContext = myDataSet;"
My object was already created, and I did set the DataContext in the code. All I had to do was leave the ListView ItemSource as {Binding} and each time I added to the Tests object, the list updated..
I can't believe I spent an entire day doubting I was doing this correctly without moving forward to check. :-)