2012-06-22 39 views
6

Tôi đang cố gắng sử dụng AvalonEdit làm trình soạn thảo văn bản XML trong ứng dụng WPF của mình. Tuy nhiên, nó không thực hiện bất kỳ định dạng nào (như các đường lượn sóng) khi nó gặp cú pháp không hợp lệ.Hiển thị Cú pháp XML Không hợp lệ với AvalonEdit

Tôi muốn biết liệu chức năng này có thể được thực hiện bằng AvalonEdit hay không, hoặc nếu có các lựa chọn thay thế khác. Cảm ơn!

Trả lời

15

Tôi cũng đang tìm cách sử dụng cú pháp cú pháp không hợp lệ xml. Trong khi nhìn vào mã nguồn SharpDevelop tôi nhận thấy rằng báo cáo lỗi đã được thực hiện ở mức cao hơn so với kiểm soát AvalonEdit và dường như không đặc biệt thích hợp để tái sử dụng. Vì vậy, tôi đã có một lúc giải mã đủ mã để có được một POC đi. Đây là những gì tôi đã đưa ra ...

<UserControl x:Class="WpfTestApp.Xml.XmlEditor" 
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
       xmlns:avalonedit="http://icsharpcode.net/sharpdevelop/avalonedit" 
       xmlns:WpfTestApp="clr-namespace:WpfTestApp.Xml"> 

    <UserControl.CommandBindings> 
     <CommandBinding Command="WpfTestApp:XmlEditor.ValidateCommand" Executed="Validate"/> 
    </UserControl.CommandBindings> 

    <avalonedit:TextEditor Name="textEditor" FontFamily="Consolas" SyntaxHighlighting="XML" FontSize="8pt"> 
     <avalonedit:TextEditor.Options> 
      <avalonedit:TextEditorOptions ShowSpaces="True" ShowTabs="True"/> 
     </avalonedit:TextEditor.Options> 
     <avalonedit:TextEditor.ContextMenu> 
      <ContextMenu> 
       <MenuItem Command="Undo" /> 
       <MenuItem Command="Redo" /> 
       <Separator/> 
       <MenuItem Command="Cut" /> 
       <MenuItem Command="Copy" /> 
       <MenuItem Command="Paste" /> 
       <Separator/> 
       <MenuItem Command="WpfTestApp:XmlEditor.ValidateCommand" /> 
      </ContextMenu> 
     </avalonedit:TextEditor.ContextMenu> 
    </avalonedit:TextEditor> 
</UserControl> 

.

public partial class XmlEditor : UserControl 
{ 
    private static readonly ICommand validateCommand = new RoutedUICommand("Validate XML", "Validate", typeof(MainWindow), 
     new InputGestureCollection { new KeyGesture(Key.V, ModifierKeys.Control | ModifierKeys.Shift) }); 

    private readonly TextMarkerService textMarkerService; 
    private ToolTip toolTip; 

    public static ICommand ValidateCommand 
    { 
     get { return validateCommand; } 
    } 

    public XmlEditor() 
    { 
     InitializeComponent(); 

     textMarkerService = new TextMarkerService(textEditor); 
     TextView textView = textEditor.TextArea.TextView; 
     textView.BackgroundRenderers.Add(textMarkerService); 
     textView.LineTransformers.Add(textMarkerService); 
     textView.Services.AddService(typeof(TextMarkerService), textMarkerService); 

     textView.MouseHover += MouseHover; 
     textView.MouseHoverStopped += TextEditorMouseHoverStopped; 
     textView.VisualLinesChanged += VisualLinesChanged; 
    } 

    private void MouseHover(object sender, MouseEventArgs e) 
    { 
     var pos = textEditor.TextArea.TextView.GetPositionFloor(e.GetPosition(textEditor.TextArea.TextView) + textEditor.TextArea.TextView.ScrollOffset); 
     bool inDocument = pos.HasValue; 
     if (inDocument) 
     { 
      TextLocation logicalPosition = pos.Value.Location; 
      int offset = textEditor.Document.GetOffset(logicalPosition); 

      var markersAtOffset = textMarkerService.GetMarkersAtOffset(offset); 
      TextMarkerService.TextMarker markerWithToolTip = markersAtOffset.FirstOrDefault(marker => marker.ToolTip != null); 

      if (markerWithToolTip != null) 
      { 
       if (toolTip == null) 
       { 
        toolTip = new ToolTip(); 
        toolTip.Closed += ToolTipClosed; 
        toolTip.PlacementTarget = this; 
        toolTip.Content = new TextBlock 
        { 
         Text = markerWithToolTip.ToolTip, 
         TextWrapping = TextWrapping.Wrap 
        }; 
        toolTip.IsOpen = true; 
        e.Handled = true; 
       } 
      } 
     } 
    } 

    void ToolTipClosed(object sender, RoutedEventArgs e) 
    { 
     toolTip = null; 
    } 

    void TextEditorMouseHoverStopped(object sender, MouseEventArgs e) 
    { 
     if (toolTip != null) 
     { 
      toolTip.IsOpen = false; 
      e.Handled = true; 
     } 
    } 

    private void VisualLinesChanged(object sender, EventArgs e) 
    { 
      if (toolTip != null) 
      { 
        toolTip.IsOpen = false; 
      } 
    } 

    private void Validate(object sender, ExecutedRoutedEventArgs e) 
    { 
     IServiceProvider sp = textEditor; 
     var markerService = (TextMarkerService)sp.GetService(typeof(TextMarkerService)); 
     markerService.Clear(); 

     try 
     { 
      var document = new XmlDocument { XmlResolver = null }; 
      document.LoadXml(textEditor.Document.Text); 
     } 
     catch (XmlException ex) 
     { 
      DisplayValidationError(ex.Message, ex.LinePosition, ex.LineNumber); 
     } 
    } 

    private void DisplayValidationError(string message, int linePosition, int lineNumber) 
    { 
     if (lineNumber >= 1 && lineNumber <= textEditor.Document.LineCount) 
     { 
      int offset = textEditor.Document.GetOffset(new TextLocation(lineNumber, linePosition)); 
      int endOffset = TextUtilities.GetNextCaretPosition(textEditor.Document, offset, System.Windows.Documents.LogicalDirection.Forward, CaretPositioningMode.WordBorderOrSymbol); 
      if (endOffset < 0) 
      { 
       endOffset = textEditor.Document.TextLength; 
      } 
      int length = endOffset - offset; 

      if (length < 2) 
      { 
       length = Math.Min(2, textEditor.Document.TextLength - offset); 
      } 

      textMarkerService.Create(offset, length, message); 
     } 
    } 
} 

.

public class TextMarkerService : IBackgroundRenderer, IVisualLineTransformer 
{ 
    private readonly TextEditor textEditor; 
    private readonly TextSegmentCollection<TextMarker> markers; 

    public sealed class TextMarker : TextSegment 
    { 
     public TextMarker(int startOffset, int length) 
     { 
      StartOffset = startOffset; 
      Length = length; 
     } 

     public Color? BackgroundColor { get; set; } 
     public Color MarkerColor { get; set; } 
     public string ToolTip { get; set; } 
    } 

    public TextMarkerService(TextEditor textEditor) 
    { 
     this.textEditor = textEditor; 
     markers = new TextSegmentCollection<TextMarker>(textEditor.Document); 
    } 

    public void Draw(TextView textView, DrawingContext drawingContext) 
    { 
     if (markers == null || !textView.VisualLinesValid) 
     { 
      return; 
     } 
     var visualLines = textView.VisualLines; 
     if (visualLines.Count == 0) 
     { 
      return; 
     } 
     int viewStart = visualLines.First().FirstDocumentLine.Offset; 
     int viewEnd = visualLines.Last().LastDocumentLine.EndOffset; 
     foreach (TextMarker marker in markers.FindOverlappingSegments(viewStart, viewEnd - viewStart)) 
     { 
      if (marker.BackgroundColor != null) 
      { 
       var geoBuilder = new BackgroundGeometryBuilder {AlignToWholePixels = true, CornerRadius = 3}; 
       geoBuilder.AddSegment(textView, marker); 
       Geometry geometry = geoBuilder.CreateGeometry(); 
       if (geometry != null) 
       { 
        Color color = marker.BackgroundColor.Value; 
        var brush = new SolidColorBrush(color); 
        brush.Freeze(); 
        drawingContext.DrawGeometry(brush, null, geometry); 
       } 
      } 
      foreach (Rect r in BackgroundGeometryBuilder.GetRectsForSegment(textView, marker)) 
      { 
       Point startPoint = r.BottomLeft; 
       Point endPoint = r.BottomRight; 

       var usedPen = new Pen(new SolidColorBrush(marker.MarkerColor), 1); 
       usedPen.Freeze(); 
       const double offset = 2.5; 

       int count = Math.Max((int) ((endPoint.X - startPoint.X)/offset) + 1, 4); 

       var geometry = new StreamGeometry(); 

       using (StreamGeometryContext ctx = geometry.Open()) 
       { 
        ctx.BeginFigure(startPoint, false, false); 
        ctx.PolyLineTo(CreatePoints(startPoint, endPoint, offset, count).ToArray(), true, false); 
       } 

       geometry.Freeze(); 

       drawingContext.DrawGeometry(Brushes.Transparent, usedPen, geometry); 
       break; 
      } 
     } 
    } 

    public KnownLayer Layer 
    { 
     get { return KnownLayer.Selection; } 
    } 

    public void Transform(ITextRunConstructionContext context, IList<VisualLineElement> elements) 
    {} 

    private IEnumerable<Point> CreatePoints(Point start, Point end, double offset, int count) 
    { 
     for (int i = 0; i < count; i++) 
     { 
      yield return new Point(start.X + (i*offset), start.Y - ((i + 1)%2 == 0 ? offset : 0)); 
     } 
    } 

    public void Clear() 
    { 
     foreach (TextMarker m in markers) 
     { 
      Remove(m); 
     } 
    } 

    private void Remove(TextMarker marker) 
    { 
     if (markers.Remove(marker)) 
     { 
      Redraw(marker); 
     } 
    } 

    private void Redraw(ISegment segment) 
    { 
     textEditor.TextArea.TextView.Redraw(segment); 
    } 

    public void Create(int offset, int length, string message) 
    { 
     var m = new TextMarker(offset, length); 
     markers.Add(m); 
     m.MarkerColor = Colors.Red; 
     m.ToolTip = message; 
     Redraw(m); 
    } 

    public IEnumerable<TextMarker> GetMarkersAtOffset(int offset) 
    { 
     return markers == null ? Enumerable.Empty<TextMarker>() : markers.FindSegmentsContaining(offset); 
    } 
} 
Các vấn đề liên quan