Привязка данных WPF очень медленная, когда я использую Convert для форматирования даты - PullRequest
4 голосов
/ 13 августа 2011

Я пишу простое приложение для отображения некоторых данных в DataGrid.Данные - это просто измерение (с плавающей точкой) и отметка времени.Отметка времени в формате uint и в секундах с 2000 года.

Я успешно выполнил задачу, но заметил, что отображение сетки данных занимает много времени (~ 1 минута).Есть около 20000 данных.Я бы не подумал, что 20 000 датумов, состоящих из уинта и поплавка, были именно такими.Следующий запрос состоял в том, чтобы отображать время как отформатированное время вместо секунд с 2000 года. Это я сделал, сделав XAML похожим на это:

<kit:DataGridTextColumn Header="FilteredValue" Binding="{Binding Path=FilteredValue}" />
<kit:DataGridTextColumn Header="Timestamp" Binding="{Binding Path=Timestamp, Converter={StaticResource TimeConverter}}" CanUserSort="False" />

Метод TimeConverter выглядит так:

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    DateTime currentDateTime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    currentDateTime = currentDateTime.AddSeconds((uint)value);
    return currentDateTime.ToString();                  
}

Это тоже работало нормально.Однако оказывается, что некоторые необработанные данные могут быть 0xFFFFFFFF.Это означает, что нет данных или недействительных данных.В этом случае я не хочу конвертировать в дату.Поэтому я написал:

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{

    if ((uint)value == 0xFFFFFFFF)
    {
    // don't bother to convert
    return ((uint)value).ToString("X");
    }
   else
   {
    DateTime currentDateTime = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    currentDateTime = currentDateTime.AddSeconds((uint)value);
    return currentDateTime.ToString();
   }

}

Опять же, это работает, но это очень медленно .Медленнее, чем оригинал и занимает около 10 минут.Я был очень удивлен этим.Это просто тот случай, когда дополнительный код выполняется 23 000 раз?1. Что я должен делать?Могу ли я что-то сделать в XAML, чтобы мой конвертер не вызывался, если не нужно?2. Когда у меня есть 0xFFFFFFFF для одного из измерений (FilteredValues), оно отображается как NaN.Это, вероятно, нормально, но было бы неплохо просто показать 0xFFFFFFFF или «нет данных».Я думаю, что он устанавливается в NaN, потому что базовый тип данных - это число с плавающей точкой.

Любые идеи?

Спасибо, Дейв

Вот XAML.Последний Датагрид представляет интерес.Обратите внимание, что я даже установил «IsVirtualizing» на True ». Также обратите внимание на использование ScrollViewer. Я сделал это, потому что в противном случае я не могу видеть все строки в последней сетке (когда она наконец-то) отображается. Удаление этого не ускорило процесс.

<Window x:Class="STDatabaseReader.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit" xmlns:kit="http://schemas.microsoft.com/wpf/2008/toolkit" xmlns:local="clr-namespace:STDatabaseReader"
    Title="Smart Transmitter Database Reader">
<Window.Resources>

    <local:BytesToStringConverter x:Key="BytesToStringConverter"></local:BytesToStringConverter>
    <local:TimeConverter x:Key="TimeConverter"></local:TimeConverter>
</Window.Resources>

<Grid>
     <ScrollViewer>    
        <StackPanel Orientation="Vertical">
            <Button Name="m_btnFetchData" HorizontalAlignment="Left" Click="m_btnFetchData_Click">Fetch File</Button>
            <StackPanel Orientation="Horizontal">

                <StackPanel Orientation="Vertical">
                    <Label HorizontalAlignment="Center">Partition 1</Label>
                    <kit:DataGrid Name="m_gridPartion1" AutoGenerateColumns="False">
                        <kit:DataGrid.Columns>
                            <kit:DataGridTextColumn Header="Header Info" Binding="{Binding Path=HeaderInfo, Converter={StaticResource BytesToStringConverter}}" CanUserSort="False" />
                            <kit:DataGridTextColumn Header="Transmitter Id" Binding="{Binding Path=TransmitterId, Converter={StaticResource BytesToStringConverter}}" CanUserSort="False" />
                            <kit:DataGridTextColumn Header="DeviceNumber" Binding="{Binding Path=DeviceNumber}" />
                            <kit:DataGridTextColumn Header="HardwareVersion" Binding="{Binding Path=HardwareVersion}" />
                            <kit:DataGridTextColumn Header="CRC" Binding="{Binding Path=CRC}" />
                        </kit:DataGrid.Columns>
                    </kit:DataGrid>
                </StackPanel>

                <StackPanel Orientation="Vertical">
                    <Label HorizontalAlignment="Center">Partition 3</Label>
                    <kit:DataGrid Name="m_gridPartion3" AutoGenerateColumns="False">
                        <kit:DataGrid.Columns>
                            <kit:DataGridTextColumn Header="Header Info" Binding="{Binding Path=HeaderInfo, Converter={StaticResource BytesToStringConverter}}" CanUserSort="False" />
                            <kit:DataGridTextColumn Header="SystemTime" Binding="{Binding Path=SystemTime, Converter={StaticResource TimeConverter}}" />
                        </kit:DataGrid.Columns>
                    </kit:DataGrid>
                </StackPanel>
            </StackPanel>
            <StackPanel Orientation="Vertical">
                <Label HorizontalAlignment="Center">Partition 2</Label>
                <kit:DataGrid Name="m_gridPartion2" AutoGenerateColumns="False">
                    <kit:DataGrid.Columns>
                        <kit:DataGridTextColumn Header="Header Info" Binding="{Binding Path=HeaderInfo, Converter={StaticResource BytesToStringConverter}}" CanUserSort="False" />

                        <kit:DataGridTextColumn Header="FirmwareRevision" Binding="{Binding Path=FirmwareRevision, Converter={StaticResource BytesToStringConverter}}" CanUserSort="False" />

                        <kit:DataGridTextColumn Header="SoftwarePartNumber" Binding="{Binding Path=SoftwarePartNumber, Converter={StaticResource BytesToStringConverter}}" CanUserSort="False" />

                        <kit:DataGridTextColumn Header="FirmwareUpgradeTime" Binding="{Binding Path=FirmwareUpgradeTime,Converter={StaticResource TimeConverter}}" />
                        <kit:DataGridTextColumn Header="DatabaseEraseTime" Binding="{Binding Path=DatabaseEraseTime,Converter={StaticResource TimeConverter}}" />
                        <kit:DataGridTextColumn Header="RangeEnzymeElectrode" Binding="{Binding Path=RangeEnzymeElectrode}" />
                        <kit:DataGridTextColumn Header="OffsetEnzymeElectrode" Binding="{Binding Path=OffsetEnzymeElectrode}" />
                        <kit:DataGridTextColumn Header="BiasValue" Binding="{Binding Path=BiasValue}" />
                    </kit:DataGrid.Columns>
                </kit:DataGrid>
            </StackPanel>

            <StackPanel Orientation="Horizontal">
                <StackPanel Orientation="Vertical">
                    <Label HorizontalAlignment="Center">Partition 4 - HeaderInfo</Label>
                    <kit:DataGrid Name="m_gridDataHeader" AutoGenerateColumns="False">
                        <kit:DataGrid.Columns>
                            <kit:DataGridTextColumn Header="Header Info" Binding="{Binding Path=HeaderInfo, Converter={StaticResource BytesToStringConverter}}" CanUserSort="False" />
                        </kit:DataGrid.Columns>
                    </kit:DataGrid>
                </StackPanel>
                <StackPanel   Orientation="Vertical">
                    <Label HorizontalAlignment="Center">Partition 4 - Chemistry Data</Label>

                        <kit:DataGrid Name="m_gridData" AutoGenerateColumns="False" VirtualizingStackPanel.IsVirtualizing="True"  Loaded="m_gridData_Loaded">
                            <kit:DataGrid.Columns>
                                <!--
                                <kit:DataGridTextColumn Header="Noise" Binding="{Binding Path=Noise, StringFormat=\{0:X8\}}" />
                                <kit:DataGridTextColumn Header="FilteredValue" Binding="{Binding Path=FilteredValue, StringFormat='X'}" />
                                 <kit:DataGridTextColumn Header="Timestamp" Binding="{Binding Path=Timestamp, StringFormat=\{0:X\}}" />   -->
                            <kit:DataGridTextColumn Header="Noise" Binding="{Binding Path=Noise}" />
                            <kit:DataGridTextColumn Header="FilteredValue" Binding="{Binding Path=FilteredValue}" />
                            <kit:DataGridTextColumn Header="Timestamp" Binding="{Binding Path=Timestamp, Converter={StaticResource TimeConverter}}" CanUserSort="False" />
                        </kit:DataGrid.Columns>
                        </kit:DataGrid>

                </StackPanel >
            </StackPanel>
        </StackPanel>
     </ScrollViewer>     
</Grid>

1 Ответ

2 голосов
/ 13 августа 2011

Так как столбец DataGridTextColumn, вы можете заставить его отображать 0xFFFFFFFF, просто возвращая его в конвертере

if ((uint)value == 0xFFFFFFFF)
{
    // don't bother to convert
    return "0xFFFFFFFF";
}

Что касается медленного DataGrid, он должен использовать VirtualizingStackPanel по умолчанию, поэтому, если вы не изменили его, он должен быть довольно быстрым, поскольку вы будете работать только с DataGridRows, которые видимый пользователю в данный момент. Также код в конвертере должен занимать практически не время.

Таким образом, наиболее вероятная причина медленного DataGrid - это то, что вы изменили ItemsPanel на что-то другое, чем VirtualizingStackPanel или как-то отключили виртуализацию, но трудно понять, не видя, как ваш DataGrid определяется

Редактировать
Запустите следующий код после завершения загрузки DataGrid, например, в событии Loaded для DataGrid. Если MessageBox отображает большое число (не должно превышать 50), то у вас есть источник вашей проблемы.

private void DataGrid_Loaded(object sender, RoutedEventArgs e)
{
    DataGrid dataGrid = sender as DataGrid;
    List<DataGridRow> generatedDataGridRows = VisualTreeHelpers.GetVisualChildCollection<DataGridRow>(dataGrid);
    MessageBox.Show(generatedDataGridRows.Count.ToString());
}
public static List<T> GetVisualChildCollection<T>(object parent) where T : Visual
{
    List<T> visualCollection = new List<T>();
    GetVisualChildCollection(parent as DependencyObject, visualCollection);
    return visualCollection;
}
private static void GetVisualChildCollection<T>(DependencyObject parent, List<T> visualCollection) where T : Visual
{
    int count = VisualTreeHelper.GetChildrenCount(parent);
    for (int i = 0; i < count; i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(parent, i);
        if (child is T)
        {
            visualCollection.Add(child as T);
        }
        else if (child != null)
        {
            GetVisualChildCollection(child, visualCollection);
        }
    }
}

Например, использование StackPanel в качестве родительской панели будет очень медленным, поскольку DataGrid может занимать неограниченное вертикальное пространство, поэтому будут сгенерированы все строки

<StackPanel>
    <!-- Slow DataGrid with 20000+ items in ItemsSource -->
    <DataGrid ...>
</StackPanel>

, но использование Grid будет очень быстрым, поскольку высота DataGrid будет ограничена, поэтому можно использовать виртуализацию

<Grid>
    <!-- Fast DataGrid with 20000+ items in ItemsSource -->
    <DataGrid ...>
</Grid>
...