2012-06-22 15 views
6

Próbuję użyć AvalonEdit jako edytora tekstu XML w mojej aplikacji WPF. Jednak nie wykonuje żadnego formatowania (na przykład falistych linii), gdy napotka nieprawidłową składnię.Wyświetlanie nieprawidłowej składni XML z AvalonEdit

Chciałbym wiedzieć, czy taką funkcję można wykonać za pomocą AvalonEdit, czy też istnieją inne alternatywy. Dzięki!

Odpowiedz

15

Chciałbym również wykorzystać podświetlanie składni nieprawidłowych xml. Patrząc na kod źródłowy SharpDevelop zauważyłem, że zgłaszanie błędów odbywało się na poziomie wyższym niż kontrola AvalonEdit i nie wydawało się szczególnie przydatne do ponownego użycia. Więc musiałem spróbować wydobyć wystarczającą ilość kodu, aby uruchomić POC. Oto, co wymyśliłem ...

<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); 
    } 
} 
Powiązane problemy