AvalonEdit簡介
AvalonEdit是基於WPF開發的程式碼顯示控制元件,預設支援多種不同語言的關鍵詞高亮,並且可以自定義高亮配置。所以通過AvalonEdit可以快速開發出自己想要的程式碼編輯器。
通過Nuget安裝AvalonEdit,並在頁面新增控制元件
<avalonEdit:TextEditor
xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
Name="TextEditor"
SyntaxHighlighting="C#"
FontFamily="Consolas"
FontSize="14"
WordWrap ="True"
LineNumbersForeground="#FF2B91AF"
ShowLineNumbers="True">
<avalonEdit:TextEditor.Options>
<avalonEdit:TextEditorOptions ShowSpaces="True" WordWrapIndentation="4" InheritWordWrapIndentation="true">
<avalonEdit:TextEditorOptions.ColumnRulerPosition>
<system:Int32>10</system:Int32>
</avalonEdit:TextEditorOptions.ColumnRulerPosition>
</avalonEdit:TextEditorOptions>
</avalonEdit:TextEditor.Options>
</avalonEdit:TextEditor>
引數含義
-
xmlns:avalonEdit
:名稱空間,也可以直接寫在呼叫該控制元件的窗體內 -
SyntaxHighlighting
:設定高亮 -
ShowLineNumbers
:是否顯示行號 -
LineNumbersForeground
:設定編輯器行號顏色 -
ShowSpaces
:是否顯示空格 -
WordWrapIndentation
:換行縮排距離 -
InheritWordWrapIndentation
:是否繼承上一行的換行縮排
自定義高亮配置
比如AvalonEdit預設支援的sql語法高亮不夠強大或者不符合要求,可以自定義配置註冊進行。
首先,在專案中引入一個.xshd
檔案,具體規則可以參考官網介紹。這裡提供一個sql.xshd
檔案,描述了對SQL語法的高亮支援。
在程式啟動的時候讀取該配置檔案,並註冊到AvalonEdit
中。
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(Assembly.GetExecutingAssembly().GetName().Name + ".sql.xshd"))
{
using (var reader = new System.Xml.XmlTextReader(stream))
{
var sqlDefinition = HighlightingLoader.Load(reader, HighlightingManager.Instance);
HighlightingManager.Instance.RegisterHighlighting("SQL", new string[] { ".sql" }, sqlDefinition);
}
}
ps:這裡是將sql.xshd
的生成操作設定為了嵌入的資源,也可以將其輸出到目錄再讀取內容。
如果需要在專案執行過程中,切換高亮語法支援,可以這麼做
textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("SQL");
看下高亮效果:
MVVM繫結AvalonEdit文字內容
當我嘗試直接將ViewModel中的字串直接繫結到Text上時,編輯器就報錯了,因為Text屬性並不是可以直接繫結的依賴屬性。
Object of type 'System.Windows.Data.Binding' cannot be converted to type 'System.String'.
最後鼓搗一通後的解決方案是使用Microsoft.Xaml.Behaviors
包(其實是微軟以前System.Windows.Interactivity
的開源版)。
xaml程式碼
<avalonEdit:TextEditor Name="TextEditor">
<i:Interaction.Behaviors>
<local:AvalonEditBehaviour InputText="{Binding InputString}"/>
</i:Interaction.Behaviors>
</avalonEdit:TextEditor>
後端程式碼
public sealed class AvalonEditBehaviour : Behavior<TextEditor>
{
public static readonly DependencyProperty InputTextProperty =
DependencyProperty.Register("InputText", typeof(string), typeof(AvalonEditBehaviour),
new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null));
public string InputText
{
get { return (string)GetValue(InputTextProperty); }
set { SetValue(InputTextProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
if (AssociatedObject != null)
{
AssociatedObject.TextChanged += AssociatedObjectOnTextChanged;
}
}
protected override void OnDetaching()
{
base.OnDetaching();
if (AssociatedObject != null)
{
AssociatedObject.TextChanged -= AssociatedObjectOnTextChanged;
}
}
private void AssociatedObjectOnTextChanged(object sender, EventArgs eventArgs)
{
var textEditor = sender as TextEditor;
if (textEditor != null)
{
if (textEditor.Document != null)
{
InputText = textEditor.Document.Text;
}
}
}
}
最後看一下實際效果吧