Ужасная производительность при использовании ListViews с вложенными объектами в WPF - PullRequest
3 голосов
/ 12 января 2011

, как упомянуто в названии, я получаю ужасную производительность, если использую ListView s с вложенными объектами.Мой сценарий таков: каждая строка ListView представляет объект класса Transaction со следующими атрибутами:

private int mTransactionID;
private IBTTransactionSender mSender;
private IBTTransactionReceiver mReceiver;
private BTSubstrate mSubstrate;
private double mAmount;
private string mDeliveryNote;
private string mNote;
private DateTime mTransactionDate;
private DateTime mCreationTimestamp;
private BTEmployee mEmployee;
private bool mImported;
private bool mDescendedFromRecurringTransaction;

Доступ к каждому атрибуту можно получить с помощью соответствующего свойства.ObservableCollection<Transaction> связан с ItemsSource из ListView.Сам ListView выглядит следующим образом:

        </ListView.GroupStyle>
        <ListView.View>
            <GridView>

                <GridViewColumn core:SortableListView.SortPropertyName="Transaction.ToSave" Width="80">
                    <GridViewColumnHeader Name="GVCHLoadedToSave" Style="{StaticResource ListViewHeaderStyle}">Speichern</GridViewColumnHeader>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Grid>
                                <CheckBox Name="CBListViewItem" IsChecked="{Binding Path=Transaction.ToSave, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></CheckBox>
                            </Grid>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

                <GridViewColumn core:SortableListView.SortPropertyName="Transaction.TransactionDate" Width="80">
                    <GridViewColumnHeader Name="GVCHLoadedDate" Style="{StaticResource ListViewHeaderStyle}">Datum</GridViewColumnHeader>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Grid>
                                <TextBlock Text="{Binding ElementName=DPDate, Path=Text}" Style="{StaticResource GridBlockStyle}"/>
                                <toolkit:DatePicker Name="DPDate" 
                                                                Width="{Binding ElementName=GVCHDate, Path=ActualWidth}"
                                                                SelectedDateFormat="Short" 
                                                                Style="{StaticResource GridEditStyle}" 
                                                                SelectedDate="{Binding Path=Transaction.TransactionDate, Mode=TwoWay}"
                                                                SelectedDateChanged="DPDate_SelectedDateChanged"/>
                            </Grid>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

                <GridViewColumn core:SortableListView.SortPropertyName="Transaction.Sender.Description" Width="120">
                    <GridViewColumnHeader Name="GVCHLoadedSender" Style="{StaticResource ListViewHeaderStyle}">Von</GridViewColumnHeader>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Grid>
                                <TextBlock Text="{Binding Path=Transaction.Sender.Description}" Style="{StaticResource GridBlockStyle}"/>
                                <ComboBox Name="CBSender"
                                                      Width="{Binding ElementName=GVCHSender, Path=ActualWidth}"
                                                      SelectedItem="{Binding Path=Transaction.Sender}"
                                                      DisplayMemberPath="Description"
                                                      Text="{Binding Path=Sender.Description, Mode=OneWay}"
                                                      ItemsSource="{Binding ElementName=Transaction, Path=SenderList}"
                                                      Style="{StaticResource GridEditStyle}">
                                </ComboBox>
                            </Grid>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

                <GridViewColumn core:SortableListView.SortPropertyName="Transaction.Receiver.Description" Width="120">
                    <GridViewColumnHeader Name="GVCHLoadedReceiver" Style="{StaticResource ListViewHeaderStyle}">Nach</GridViewColumnHeader>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Grid>
                                <TextBlock Text="{Binding Path=Transaction.Receiver.Description}" Style="{StaticResource GridBlockStyle}"/>
                                <ComboBox Name="CBReceiver"
                                                      Width="{Binding ElementName=GVCHReceiver, Path=ActualWidth}"
                                                      SelectedItem="{Binding Path=Transaction.Receiver}"
                                                      DisplayMemberPath="Description"
                                                      Text="{Binding Path=Receiver.Description, Mode=OneWay}"
                                                      ItemsSource="{Binding ElementName=Transaction, Path=ReceiverList}"
                                                      Style="{StaticResource GridEditStyle}">
                                </ComboBox>
                            </Grid>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

                <GridViewColumn core:SortableListView.SortPropertyName="Transaction.Substrate.Description" Width="140">
                    <GridViewColumnHeader Name="GVCHLoadedSubstrate" Style="{StaticResource ListViewHeaderStyle}">Substrat</GridViewColumnHeader>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Grid>
                                <TextBlock Text="{Binding Path=Transaction.Substrate.Description}" Style="{StaticResource GridBlockStyle}"/>
                                <ComboBox Name="CBSubstrate"
                                                      Width="{Binding ElementName=GVCHSubstrate, Path=ActualWidth}"
                                                      SelectedItem="{Binding Path=Transaction.Substrate}"
                                                      DisplayMemberPath="Description"
                                                      Text="{Binding Path=Substrate.Description, Mode=OneWay}"
                                                      ItemsSource="{Binding ElementName=Transaction, Path=SubstrateList}"
                                                      Style="{StaticResource GridEditStyle}">
                                </ComboBox>
                            </Grid>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

                <GridViewColumn core:SortableListView.SortPropertyName="Transaction.Amount" Width="80">
                    <GridViewColumnHeader Name="GVCHLoadedAmount" Style="{StaticResource ListViewHeaderStyle}">Menge [kg]</GridViewColumnHeader>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Grid>
                                <TextBlock Text="{Binding Path=Transaction.Amount}" Style="{StaticResource GridBlockStyle}"/>
                                <TextBox Name="TBAmount" Text="{Binding Path=Transaction.Amount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="{Binding ElementName=GVCHAmount, Path=ActualWidth}" Style="{StaticResource GridTextBoxStyle}" />
                            </Grid>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

                <GridViewColumn core:SortableListView.SortPropertyName="Transaction.DeliveryNote" Width="100">
                    <GridViewColumnHeader Name="GVCHLoadedDeliveryNote" Style="{StaticResource ListViewHeaderStyle}">Lieferschein Nr.</GridViewColumnHeader>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Grid>
                                <TextBlock Text="{Binding Path=Transaction.DeliveryNote}" Style="{StaticResource GridBlockStyle}"/>
                                <TextBox Name="TBDeliveryNote" Text="{Binding Path=Transaction.DeliveryNote, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="{Binding ElementName=GVCHDeliveryNote, Path=ActualWidth}" Style="{StaticResource GridEditStyle}" />
                            </Grid>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

                <GridViewColumn core:SortableListView.SortPropertyName="Transaction.Note" Width="190">
                    <GridViewColumnHeader Name="GVCHLoadedNote" Style="{StaticResource ListViewHeaderStyle}">Bemerkung</GridViewColumnHeader>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Grid>
                                <TextBlock Text="{Binding Path=Transaction.Note}" Style="{StaticResource GridBlockStyle}"/>
                                <TextBox Name="TBNote" Text="{Binding Path=Transaction.Note, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="{Binding ElementName=GVCHNote, Path=ActualWidth}" Style="{StaticResource GridEditStyle}" />
                            </Grid>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

                <GridViewColumn core:SortableListView.SortPropertyName="Transaction.Employee.LastName" Width="100">
                    <GridViewColumnHeader Name="GVCHLoadedEmployee" Style="{StaticResource ListViewHeaderStyle}">Mitarbeiter</GridViewColumnHeader>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Grid>
                                <TextBlock Text="{Binding Path=Transaction.Employee.LastName}" Style="{StaticResource GridBlockStyle}"/>
                                <ComboBox Name="CBEmployee"
                                                      Width="{Binding ElementName=GVCHEmployee, Path=ActualWidth}"
                                                      SelectedItem="{Binding Path=Transaction.Employee}"
                                                      DisplayMemberPath="LastName"
                                                      Text="{Binding Path=Employee.LastName, Mode=OneWay}"
                                                      ItemsSource="{Binding ElementName=Transaction, Path=EmployeeList}"
                                                      Style="{StaticResource GridEditStyle}">
                                </ComboBox>
                            </Grid>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

            </GridView>
        </ListView.View>
    </ListView>

Как вы можете видеть на скриншоте , пользователь получил возможностьизмените значения атрибутов транзакции с помощью комбинированных списков.

Хорошо, теперь моя проблема.Если я нажму на кнопку «Laden», приложение загрузит около 150 записей в ObservableCollection<Transaction>.Прежде чем заполнить коллекцию, я устанавливаю ItemsSource из ListView на null, а после заполнения я снова связываю коллекцию с ItemsSource.Сама загрузка занимает несколько миллисекунд, но рендеринг заполненной коллекции занимает много времени (150 записей = около 20 секунд).Я проверил, чтобы удалить все Comboboxes из xaml, и я получил лучшую производительность, потому что мне не нужно заполнять ComboBoxes для каждой строки.Но мне нужны эти комбинированные списки для изменения атрибутов Transaction.

Кто-нибудь знает, как улучшить производительность?

THX

Ответы [ 4 ]

3 голосов
/ 12 января 2011

Все ответы до сих пор, похоже, направлены на то, чтобы пользовательский интерфейс работал быстрее, в то время как мне кажется, что это сбор данных, который виноват. Я сделал подобное приложение и не было проблем с производительностью. Свойства, которые вы связываете, представляют поля или запросы?

0 голосов
/ 21 ноября 2013

У меня была та же проблема, и я наконец понял, что это связано с чем-то, не связанным с ListView или ComboBox.

Бывает, что ListView был вложен в Infragistic TabControl, и каждый раз, когда что-то связывалось внутри ListView (то есть: ComboBoxes), «SelectionChange» TabControl срабатывал, вызывая задержку ...

Я также тестировал с родным Microsft TabControl, и у меня такое же поведение, но несколько более производительное.

Я решил проблему, проверив SelectionChangedEventArgs ... убедившись, что e.AddedItems содержит только "TabItem" (а не ComboBox) перед обработкой.

Надеюсь, это поможет,

0 голосов
/ 12 января 2011

Можем ли мы взглянуть на более широкую картину вашей структуры данных? Меня беспокоит то, что некоторые комбо-боксы получают свои данные. Например,

<ComboBox Name="CBSender"
Width="{Binding ElementName=GVCHSender, Path=ActualWidth}"
SelectedItem="{Binding Path=Transaction.Sender}"
DisplayMemberPath="Description"
Text="{Binding Path=Sender.Description, Mode=OneWay}"
ItemsSource="{Binding ElementName=Transaction, Path=SenderList}"
Style="{StaticResource GridEditStyle}">

Это зависит от того, отличается ли список отправителей для каждого объекта в вашей коллекции транзакций. Если нет, я бы предложил загрузить список / коллекцию отправителей в собственный ресурс в xaml и просто перенести значения в поле со списком. Как и сейчас, каждый выпадающий список должен запросить свой соответствующий объект, получить список, а затем предварительно отобразить этот список. 4 поля со списком по 150 объектов - это 600 списков данных, которые необходимо получить и предварительно обработать отдельно друг от друга.

Если вы перетащите эти списки в ресурс XAML, вы сохраните только 4 списка.

Edit:

Также просто из любопытства. Является ли требованием к дизайну (например, клиент хочет, чтобы это выглядело так), чтобы элементы управления отображением и редактирование элементов управления были видны одновременно? Вместо этого вы можете использовать один шаблон для отображения, а другой - для редактирования, чтобы, когда запись не выбрана, все ячейки были TextBlocks, при выборе записи ячейки были элементами управления редактирования, ComboBox, TextBox, DatePicker и т. Д.

0 голосов
/ 12 января 2011

150 записей звучат не так много для меня, а 20 секунд звучат как долго. Я не вижу ничего плохого в том, что вы опубликовали, но это не значит, что там нет проблем.

Первое, на что нужно обратить внимание, это посмотреть, включена ли UI Virtualization .

Потенциально попытаться выяснить, улучшает ли производительность DataGrid вместо ListView (в комплекте с wpf4 или может быть загружен как часть WPFToolKit )?

Еще одна вещь для улучшения общей производительности (но, возможно, не решающая первоначальную проблему, если ваш пользовательский интерфейс уже использует виртуализацию пользовательского интерфейса) - это реализация Виртуализация данных . Это выглядит сложно, но статья довольно хороша в этом, и это оптимальное решение для повышения производительности рендеринга больших списков данных.

Как последний отказавшийся обходной путь , вы могли бы рассмотреть какой-то механизм подкачки страниц, чтобы вы показывали только меньшее количество элементов за один раз. Вы можете посмотреть на классическое решение для пейджинга с номерами страниц, кнопками вперед и назад.

...