加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 使用AvalonEdit显示无效的XML语法

发布时间:2020-12-15 18:30:38 所属栏目:百科 来源:网络整理
导读:我试图在我的 WPF应用程序中使用 AvalonEdit作为XML文本编辑器.但是,它遇到无效语法时不会进行任何格式化(如波浪线). 我想知道是否可以使用AvalonEdit完成此类功能,或者是否有其他替代方法.谢谢! 解决方法 我还希望利用xml无效的语法高亮显示.在查看SharpDe
我试图在我的 WPF应用程序中使用 AvalonEdit作为XML文本编辑器.但是,它遇到无效语法时不会进行任何格式化(如波浪线).

我想知道是否可以使用AvalonEdit完成此类功能,或者是否有其他替代方法.谢谢!

解决方法

我还希望利用xml无效的语法高亮显示.在查看SharpDevelop源代码时,我注意到错误报告是在比AvalonEdit控件更高的级别完成的,并且似乎不太适合重用.
所以我开始提取足够的代码来获得POC.这就是我想出来的……
<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,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);
    }
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读