简体   繁体   中英

WPF MVVM treeview performance

I'm using an MVVM pattern to display tree data for transactions.

The hierachy is Customer -> Accounts -> Transactions.

I have a base class TreeItemViewModel which implements basic dependency properties like IsExpanded, IsSelected etc which are 2 way dependency properties. Then I have a bunch of classes such as CustomerViewModel, AccountViewModel, TransactionViewModel. Each of which has a Children collection. So a CustomerViewModel will contain a list of AccountViewModels, each of which has it's own Children collection of TransactionViewModels. I am then using datatemplates to bind the XAML on to these view models, one example (transactionviewmodel template) is given below.

When you expand a node, the IsExpanded method of the base class is called, which then loads the children of the node - so it is lazy load. Furthermore when you expand a Customer node, it not only loads the accounts, but also the transactions for each account as they are needed for a few pieces of statistic data. So when you expand the account node, all it has to do is render the transactionviewmodels according to the datatemplate bindings, as the actual children (transactionviewmodel objects) are already loaded by that point.

Its this rendering of transactions which is extremely slow, especially for large volumes.

I have tested it with one customer, one account which has 6000 transactions and commenting out most of the datatemplate. The timings are as follows

Non image columns only included - 26s All columns, changing the image converter class to return null instead of an image - 51s All columns including images : 20 minutes

So my concerns are

1) First of all 27s to load 6000 transactions seems very slow and this is without any images involved. Is it my design thats slowing it down and how could I do it better without rearchitecting the whole thing? (not an option at this point)

2) How can I speed up the image processing so it doesnt hang the applcation?

Ok code below for Transaction data template


                <Grid.RowDefinitions>
                    <RowDefinition Height="18" />
                </Grid.RowDefinitions>

                <TextBlock Text="{Binding TransactionId}" />
                <TextBlock Grid.Column="1" Text="{Binding CutOffDate}" />
                <Image Grid.Column="2" Source="{Binding AlarmImg, Converter={StaticResource stringToImageConverter}}" Height="25" HorizontalAlignment="Left"/>
                <TextBlock Grid.Column="3" Text="{Binding SourceSystemDesc}" />
                <Image Grid.Column="4" Margin="0,0,0,0" Source="{Binding CcmpImg, Converter={StaticResource stringToImageConverter}}" Height="25" ToolTip="CCMP Type" HorizontalAlignment="Left"/>
                <Image Grid.Column="5" Margin="0,0,0,0" Source="{Binding PrintedImg, Converter={StaticResource stringToImageConverter}}" Height="25" ToolTip="Printed" HorizontalAlignment="Left"/>
                <Image Grid.Column="6" Margin="0,0,0,0" Source="{Binding AppRejImg, Converter={StaticResource stringToImageConverter}}" Height="25" ToolTip="Approved/Rejected" HorizontalAlignment="Left"/>
                <Image Grid.Column="7" Margin="0,0,0,0" Source="{Binding LockImg, Converter={StaticResource stringToImageConverter}}" Height="25" ToolTip="Locked" HorizontalAlignment="Left"/>
                <TextBlock Grid.Column="8" Style="{StaticResource myTxnStyleColor}" Text="{Binding TxnAmount}" HorizontalAlignment="Right"/>
                <TextBlock Grid.Column="9" Text="{Binding TxnCurrency}" HorizontalAlignment="Right" />
                <Image Grid.Column="10" Margin="0,0,0,0" Source="{Binding CurrencyImg, Converter={StaticResource stringToImageConverter}}" Height="25"/>
            </Grid>
        </DataTemplate>

And finally the static resource converter frigged to return a blank image

    using System;
using System.Globalization;
using System.Runtime.Caching;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Citi.Rbcs.UI.Windows.HelperClasses
{
    public class StringToImageConverter : IValueConverter
    {
        private static readonly BitmapImage BlankImage =
            new BitmapImage(new Uri("pack://application:,,,/Resources/Images/Blank.gif"));

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            try
            {
                return BlankImage;

                var imageUrl = value as string;
                if (string.IsNullOrEmpty(imageUrl)) return BlankImage;

                // Strip out the Image name so we ignore path
                if (imageUrl.LastIndexOf(@"\") != -1)
                    imageUrl = imageUrl.Substring(imageUrl.LastIndexOf(@"\") + 1);
                if (imageUrl.LastIndexOf(@"/") != -1)
                    imageUrl = imageUrl.Substring(imageUrl.LastIndexOf(@"/") + 1);

                var image = MemoryCache.Default.Get(imageUrl) as ImageSource;

                if (image == null)
                {
                    image = new BitmapImage(new Uri("pack://application:,,,/Resources/Images/" + imageUrl));
                    MemoryCache.Default.Set(imageUrl, image, new CacheItemPolicy());
                }

                return image;
            }
            catch (Exception)
            {
                return BlankImage;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

Any help would be greatly appreciated, Ideally I would hope with a WPF tree view of this hierachichal data that you could expand a node with 6000 children in about 5-10 seconds. Whats causing it to take so long, is it just the MVVM data binding?

I assume you are not showing all the images from the XAML at the same time.

There are two possible reasons to be slow:

1 - Heavy XAML parsing and loading is very very slow

2 - Binding is great, but are sequential and a binding locks other bindings and so on.

You can try:

1 - Do not add at desing time innecesary controls into your XAML, add it at runtime creating the controls dinamically, is faster (or fastest). Do it even these are not showing, remember that the problem is in parsing and loading.

2 - Try to use Asynchronous binding if possible. In this way a binding does not lock each other.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM