2014-04-05 1 views
0

일부 캡처 된 네트워크 데이터 (이진 파일)의 WinForms 디스플레이가 있습니다. 나는 WPF로 학습 운동으로 옮기려고 노력하고 있으며 앞으로 더 많은 관리가 가능해야하기 때문에 노력하고 있습니다.이진 데이터 표시에 적합한 WPF 레이아웃

이진 데이터의 가장 일반적인 표시 형식은 왼쪽의 각 값의 숫자 값과 오른쪽의 인쇄 가능한 ASCII 문자에 해당하는 8 진수의 문자열 표시를 모두 보여주는 이중 열 레이아웃입니다.

또한 각 값의 색을 독립적으로 변경하여 감지 된 변경 사항을 강조 할 수 있기를 원합니다.

enter image description here

지금까지 나는 FlowDocumentTable에 의해 채워지는 RichTextBox으로 시도했습니다. TableRowGroup과 숫자 표시 용과 문자열 표시 용의 두 개의 열이 있습니다.

루프가 데이터가 변경 될 때마다 TableRowGroup을 다시 생성합니다. 16 바이트의 데이터마다 Paragraph이 포함 된 새로운 TableRow이 생성됩니다. TableCell 내의 값은 각각 Run으로 관리됩니다. Run 색칠이 필요한 값에 해당하는 객체는 에 배치되기 전에 Span에 더 중첩됩니다.

이 기능은 작동하지만 보통 데이터 크기의 WinForms RichTextBox보다 눈에 띄게 느립니다. WPF는 ~ Run 인스턴스를 하나의 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); 
    } 
+0

이것은 단순히 표시하기위한 것입니까? UI에서 값을 편집해야하는 이유가 무엇입니까? – kmacdonald

+0

@kmacdonald : 편집 할 필요가 없습니다. 그러나 다른 사소한 상호 작용이 도움이됩니다. 클립 보드에 복사. 추가 처리를위한 하위 집합 선택. 수색. –

+0

@kmacdonald : 색상 화는 위치 지정에 영향을 미치지 않으므로 단락마다 하나의'Run'으로이 작업을 수행하고 렌더링을 무시할 수 있습니까? 대신에'Grid' 또는'ListView'를 사용해야합니까? –

답변

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, 색상 및 기타 UI 업데이트를 변경하는 경우 IValueConverter.