我有一些WinForms显示一些捕获的网络数据(二进制)。我正在尝试将WPF作为一项学习练习,并且因为它应该更加可维护。
正如您所看到的,二进制数据最常见的显示格式是双列布局,显示左侧每个值的数值和右侧可打印ASCII字符对应的八位字节的字符串显示。
我还希望能够独立更改每个值的颜色以突出显示检测到的更改。
到目前为止,我已尝试使用RichTextBox
,其中FlowDocument
由Table
填充。有一个TableRowGroup
和两列,一列用于数字显示,一列用于字符串显示。
每次数据更改时,循环都会重新创建TableRowGroup
。每16个字节的数据形成一个包含单个TableRow
的新Paragraph
。 TableCell
中的值分别作为Run
进行管理。对应于需要着色的值的Run
个对象在放入Span
之前会进一步嵌套在Paragraph
中。
这适用,但对于中等数据大小,明显慢于WinForms RichTextBox
。我认为WPF并非真正设计为在一个Run
中有~4000 FlowDocument
个实例。
另外,我被告知必须创建如此多的TableCell
和Run
对象并不是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);
}
答案 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更新。