c# - 当我使用 Convert 格式化日期时,WPF DataBinding 非常慢

标签 c# wpf datagrid converters ivalueconverter

我正在编写一个简单的应用程序来在 DataGrid 上显示一些数据。数据只是一个测量值( float )和一个时间戳。时间戳是一个 uint,自 2000 年以来以秒为单位。

我成功完成了任务,但确实注意到显示数据网格需要很长时间(约 1 分钟)。大约有20,000条数据。我不认为由一个 uint 和一个 float 组成的 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 中做一些事情,以便在不需要时不调用我的 Converter 吗? 2. 当其中一项测量值 (FilteredValues) 为 0xFFFFFFFF 时,它显示为 NaN。这可能没问题,但只显示 0xFFFFFFFF 或“无数据”会很好。我认为它已设置为 NaN,因为基础数据类型是 float 。

有什么想法吗?

谢谢, 戴夫

这是 XAML。最后一个 Datagrid 是感兴趣的。请注意,我什至将“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>

最佳答案

由于该列是 DataGridTextColumn,您可以通过在转换器中返回它来使其显示 0xFFFFFFFF

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

至于 DataGrid 变慢,它应该默认使用 VirtualizingStackPanel 所以如果你没有改变它那么它应该很快,因为你会仅使用当前对用户可见的 DataGridRows。此外,转换器中的代码几乎不需要任何时间。

因此,您的 DataGrid 运行缓慢的最可能原因可能是您将 ItemsPanel 更改为 VirtualizingStackPanel 之外的其他内容,或者以某种方式禁用了虚拟化,但如果不查看您的 DataGrid 是如何定义的,就很难判断

编辑
DataGrid 完成加载后运行以下代码,例如在 DataGridLoaded 事件中。如果 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>

关于c# - 当我使用 Convert 格式化日期时,WPF DataBinding 非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7047140/

相关文章:

c# - LINQ to Entities 不支持指定的类型成员 'Items'

C# 如何获取目录的名称而不是路径?

c# - ItemPanel 模板中的统一网格

.net - 在数据模板中查找文本框

c# - 如何在绑定(bind) ItemsSource 时将按钮添加到 wpf 列表框

c# - XamlReader.Read 或 XamlReader.Parse 如何实例化类型以构建 wpf 树?

css - 如何在 p :dataGrid? 中显示字符串数组列表

c# - 将键/值对列表转换为数据表

wpf - 使用 WPF 在 F# 中使用 INotifyPropertyChanged 和 INotifyCollectionChanged

c# - 我如何最小起订量 System.IO.FileInfo 类...或任何其他没有接口(interface)的类?