WPF布局适合显示二进制数据

时间:2014-04-05 20:12:50

标签: c# wpf performance grid-layout flowdocument

我有一些WinForms显示一些捕获的网络数据(二进制)。我正在尝试将WPF作为一项学习练习,并且因为它应该更加可维护。

正如您所看到的,二进制数据最常见的显示格式是双列布局,显示左侧每个值的数值和右侧可打印ASCII字符对应的八位字节的字符串显示。

我还希望能够独立更改每个值的颜色以突出显示检测到的更改。

enter image description here

到目前为止,我已尝试使用RichTextBox,其中FlowDocumentTable填充。有一个TableRowGroup和两列,一列用于数字显示,一列用于字符串显示。

每次数据更改时,循环都会重新创建TableRowGroup。每16个字节的数据形成一个包含单个TableRow的新ParagraphTableCell中的值分别作为Run进行管理。对应于需要着色的值的Run个对象在放入Span之前会进一步嵌套在Paragraph中。

这适用,但对于中等数据大小,明显慢于WinForms RichTextBox。我认为WPF并非真正设计为在一个Run中有~4000 FlowDocument个实例。

另外,我被告知必须创建如此多的TableCellRun对象并不是WPF的预期方法。但是我不太确定如何将它映射到视图模型,如何触发新数据的属性更改以及更改的显示选项。

任何建议将不胜感激。非常需要支持复制到剪贴板。

    internal void RenderMessageDiff(GameMessage oldMsg, GameMessage newMsg)
    {
        if (newMsg == null) return;

        var tablerows = new TableRowGroup();

        if (oldMsg == null) oldMsg = newMsg;

        var aby = newMsg.payload;
        for (int offset = 0; offset < aby.Length - 1; offset += 16)
        {
            var leftCol = new Paragraph();
            var rightCol = new Paragraph();
            int blocklen = Math.Min(16, aby.Length - offset);
            switch (selectDisplayFormat.SelectedValue as string)
            {
                case "u1":
                    for (int i = 0; i < blocklen; ++i)
                    {
                        if (i > 0) leftCol.Inlines.Add(new Run(","));
                        var v = new Run(aby[offset + i].ToString());
                        if (aby[offset + i] == oldMsg.payload[offset + i])
                            leftCol.Inlines.Add(v);
                        else
                            leftCol.Inlines.Add(new Span(v) { Foreground = Brushes.Red });
                    }
                    break;

                case "u2":
                    for (int i = 0; i + 1 < blocklen; i += 2)
                    {
                        if (i > 0) leftCol.Inlines.Add(new Run(","));
                        var v = new Run(newMsg.ConvertUInt16LE(offset + i).ToString());
                        if (newMsg.ConvertUInt16LE(offset + i) == oldMsg.ConvertUInt16LE(offset + i))
                            leftCol.Inlines.Add(v);
                        else
                            leftCol.Inlines.Add(new Span(v) { Foreground = Brushes.Red });
                    }
                    break;

                case "u4":
                    for (int i = 0; i + 3 < blocklen; i += 4)
                    {
                        if (i > 0) leftCol.Inlines.Add(new Run(","));
                        var v = new Run(newMsg.ConvertUInt32LE(offset + i).ToString());
                        if (newMsg.ConvertUInt32LE(offset + i) == oldMsg.ConvertUInt32LE(offset + i))
                            leftCol.Inlines.Add(v);
                        else
                            leftCol.Inlines.Add(new Span(v) { Foreground = Brushes.Red });
                    }
                    break;
                case "x1":
                default:
                    for (int i = 0; i < blocklen; ++i)
                    {
                        if (i > 0) leftCol.Inlines.Add(new Run(" "));
                        var v = new Run(aby[offset + i].ToString("X2"));
                        if (aby[offset + i] == oldMsg.payload[offset + i])
                            leftCol.Inlines.Add(v);
                        else
                            leftCol.Inlines.Add(new Span(v) { Foreground = Brushes.Red });
                    }

                    break;
            }
            Span changeRun = null;
            for (int i = 0; i < blocklen; ++i)
            {
                char c = (char)aby[offset + i];
                if (c < 32 || c >= 127) c = '.';
                var v = new Run(c.ToString());
                if (aby[offset + i] == oldMsg.payload[offset + i])
                {
                    rightCol.Inlines.Add(v);
                    changeRun = null;
                }
                else
                {
                    if (changeRun == null)
                        rightCol.Inlines.Add(changeRun = new Span(v) { Foreground = Brushes.Red });
                    else
                        changeRun.Inlines.Add(v);
                }
            }

            tablerows.Rows.Add(new TableRow()
            {
                Cells = { new TableCell(leftCol), new TableCell(rightCol) }
            });
        }

        textMessageDiff.RowGroups.Clear();
        textMessageDiff.RowGroups.Add(tablerows);
    }

1 个答案:

答案 0 :(得分:1)

好的,这是你在WPF中可以做的粗略版本。

    <ItemsControl ItemsSource="{Binding YourBindingRows}">
    <ItemsControl.Resources>
        <DataTemplate DataType="{x:Type Row}">
            <StackPanel Orientation="Horizontal">
                <TextBlock>Text Representation Here</TextBlock>
                <ItemsControl ItemsSource="{Binding YourBindingCells}">
                    <ItemsControl.Resources>
                        <DataTemplate DataType="{x:Type Cell}">
                            <TextBlock Text="{Binding HexValue}"></TextBlock>
                        </DataTemplate>
                    </ItemsControl.Resources>
                </ItemsControl>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.Resources>
</ItemsControl>

您可以使用ValueConverters和属性Binding来更改背景颜色。这是如何实现这一目标的另一个粗略的例子:

<TextBlock Text="{Binding HexValue}"  Background="{Binding ValueChanged, Converter={StaticResource ValueChangedBackgroundConverter}}">FF</TextBlock>

这些都是粗略的例子,我认为你将不得不做更多关于如何真正利用MVVM的研究。但是,您的项目听起来很酷,我认为它在MVVM环境中可以很好地工作。有关进一步研究的信息,请参阅:INotifiedPropertyChanged用于绑定,IValueConverter用于更改颜色和其他UI更新。