C# 使用AvaloneEdit显示无效的XML语法
我试图在WPF应用程序中用作XML文本编辑器。但是,当遇到无效语法时,它不会进行任何格式化(例如波浪线)C# 使用AvaloneEdit显示无效的XML语法,c#,xml,avalonedit,C#,Xml,Avalonedit,我试图在WPF应用程序中用作XML文本编辑器。但是,当遇到无效语法时,它不会进行任何格式化(例如波浪线) 我想知道是否可以使用AvalonEdit完成此功能,或者是否有其他替代方案。谢谢 我还希望使用xml无效语法突出显示。在查看SharpDevelop源代码时,我注意到错误报告是在比AvalonEdit控件更高的级别上完成的,似乎并不特别适合重用。 所以我试着提取足够的代码来启动POC。这是我想到的 <UserControl x:Class="WpfTestApp.Xml.XmlEdit
我想知道是否可以使用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>
公共部分类XmlEditor:UserControl
{
私有静态只读ICommand validateCommand=新路由ICommand(“验证XML”、“验证”、类型(主窗口),
新的InputGestureCollection{new keypostation(Key.V,ModifierKeys.Control | ModifierKeys.Shift)});
私有只读文本标记服务文本标记服务;
私人工具提示;
公共静态ICommand ValidateCommand
{
获取{return validateCommand;}
}
公共XmlEditor()
{
初始化组件();
textMarkerService=新的textMarkerService(textEditor);
TextView TextView=textEditor.TextArea.TextView;
textView.BackgroundRenders.Add(textMarkerService);
textView.LineTransformers.Add(textMarkerService);
textView.Services.AddService(typeof(TextMarkerService),TextMarkerService);
textView.MouseHover+=MouseHover;
textView.MouseHoverStopped+=TextEditorMouseHoverStopped;
textView.VisualLinesChanged+=VisualLinesChanged;
}
专用void鼠标悬停(对象发送器,MouseEventArgs e)
{
var pos=textEditor.TextArea.TextView.GetPositionFloor(e.GetPosition(textEditor.TextArea.TextView)+textEditor.TextArea.TextView.ScrollOffset);
bool inDocument=pos.HasValue;
if(文件)
{
text位置逻辑位置=位置值位置;
int offset=textEditor.Document.GetOffset(逻辑位置);
var markersAtOffset=textMarkerService.GetMarkersAtOffset(偏移量);
TextMarkerService.TextMarker markerWithToolTip=markersAtOffset.FirstOrDefault(marker=>marker.ToolTip!=null);
如果(标记WithToolTip!=null)
{
如果(工具提示==null)
{
工具提示=新工具提示();
toolTip.Closed+=ToolTipClosed;
toolTip.PlacementTarget=此;
toolTip.Content=新文本块
{
Text=markerWithToolTip.ToolTip,
TextWrapping=TextWrapping.Wrap
};
toolTip.IsOpen=true;
e、 已处理=正确;
}
}
}
}
无效ToolIPClosed(对象发送方,RoutedEventArgs e)
{
工具提示=null;
}
void TextEditorMouseHoverStopped(对象发送者,MouseEventArgs e)
{
如果(工具提示!=null)
{
toolTip.IsOpen=false;
e、 已处理=正确;
}
}
私有void VisualLineChanged(对象发送方,事件参数e)
{
如果(工具提示!=null)
{
toolTip.IsOpen=false;
}
}
私有void验证(对象发送方,ExecutedRoutedEventArgs e)
{
IServiceProvider sp=文本编辑器;
var markerService=(TextMarkerService)sp.GetService(typeof(TextMarkerService));
markerService.Clear();
尝试
{
var document=newxmldocument{xmlsolver=null};
document.LoadXml(textEditor.document.Text);
}
捕获(XmlException-ex)
{
DisplayValidationError(例如消息、例如LinePosition、例如LineNumber);
}
}
私有void DisplayValidationError(字符串消息、int linePosition、int lineNumber)
{
如果(行号>=1&&lineNumber
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);
}
}