支持OneNote for Window10代码高亮工具
如果有同学使用过OneNote,就会发现OneNote原本是不支持代码高亮的,OneNote目前有两个常见的版本,一个是Office自带的OneNote(下面简称为OneNoteOffice),这个OneNote是支持装高亮插件的,一个就是OneNote for Window10(下面简称为OneNote10),这个OneNote是不支持装插件的,两个版本的颜值对比:
所以由于个人的习惯,我更习惯OneNote10的排布方式,OneNoteOffice虽然功能更强,但是排布上面看起来有点花里胡哨,而且我曾经由于代码高亮问题做过妥协,准备安装一个插件尝试一下能不能接受,结果就是插件也装不上去(可能是我菜吧):
后面寻求了一下OneNote10有没有现成的高亮代码软件,发现有一个叫做珍宝菜单的,然后发现有点小贵,勉强可以接受,但是付费机制有点坑爹,一台电脑要买一个激活码,这样买下去不得破产:
本着程序员刻(shi)苦(zai)钻(shi)研(qiong)的精神我开始了代码高亮的研究:
-
研究C#有没有现成的库可以实现代码高亮,期间还去研究珍宝插件的实现机制,结果核心算法是C++实在让人裂开;
-
网上在线代码高亮,研究了一下复制后存在于剪切板的html代码,灵机一动我可不可以自己拼一串Html代码出来,就这样勤勤恳恳的研究了好几天,发现我那学了一半的js还是太菜了,失败告终;
-
第二次尝试虽然失败了,但是也让我关注到了js代码高亮库SyntaxHighlighter,所以我想我只要能模拟打开网页以及全部复制的操作不就行了,经过第二次的尝试,让我知道通过浏览器直接实现比较困难(菜是原罪),所以回到我自己熟悉的领域,WPF有没有办法内嵌一个网页呢?
实际上有两种方案:
第一种是使用自带WebBriwser;
第二种是使用CefSharp包调用Chrom内核;
本着面向谷歌编程的原则,我选择了第二种方案。 -
经过一波三折我最终敲定了以WPF+CefSharp+静态html的方式来实现代码高亮的方案:
由于本人对js停留在一知半解的水平,所以采取的是js方法加C#修改html文本方式实现的:
C#代码:
/// <summary>
/// 粘贴到剪切板
/// </summary>
/// <param name="obj"></param>
private void Paste(object obj)
{
string text = Clipboard.GetText();
if (string.IsNullOrWhiteSpace(text))
{
return;
}
else
{
CodeText = text;
Render();
}
}
private void Copy(object obj)
{
var mainFrame = _chromWebBrowser.GetBrowser().MainFrame;
mainFrame.SelectAll();
mainFrame.Copy();
}
/// <summary>
/// 更改Html文件,重新加载渲染网页
/// </summary>
private void Render()
{
if (_chromWebBrowser == null) return;
var htmlLineList = File.ReadAllLines(_htmlPath, Encoding.UTF8).ToList();
string gutterStatus = GutterStatus ? "true" : "false";
for (int i = 0; i < htmlLineList.Count; i++)
{
var current = htmlLineList[i];
if (current.Contains("script") && current.Contains("codeTypeJs"))
{
htmlLineList[i] = Regex.Replace(current, "scripts.+.js", $@"scripts/shBrush{CodeLanguage}.js");
}
else if (current.Contains("CShape代碼調用方法"))
{
htmlLineList[i + 1] = $"HeightLight('{CodeStyle}','{FontFamily}',{(int)FontSize},{gutterStatus});";
}
else if (current.Contains("pre") && current.Contains("code_pre"))
{
htmlLineList[i] = $"<pre id=\"code_pre\" class=\"brush:{CodeLanguage}\">";
if (!string.IsNullOrEmpty(CodeText))
{
i++;
while (true)
{
if (i > htmlLineList.Count - 1) throw new Exception("示例代码文件Code.html已损坏");
var htmlLineValue = htmlLineList[i];
if (htmlLineValue.Contains("</pre>"))
{
break;
}
else
{
htmlLineList.RemoveAt(i);
}
}
htmlLineList.Insert(i++, CodeText);
break;
}
}
}
File.WriteAllLines(_htmlPath, htmlLineList, Encoding.UTF8);
_chromWebBrowser.Load(_htmlPath);
Thread.Sleep(100);
}
WPF xaml代码:
<Window x:Class="HeightLightCode.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:HeightLightCode.UI"
xmlns:vm="clr-namespace:HeightLightCode.ViewModels"
xmlns:cefSharp="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:vb="clr-namespace:HeightLightCode.VMBase"
mc:Ignorable="d"
Title="代码高亮软件" Height="450" MinHeight="450" MinWidth="800" WindowStyle="None" AllowsTransparency="True" x:Name="mainWin"
Width="800" Icon="..\Resources\YI32.png" ResizeMode="CanResizeWithGrip" WindowStartupLocation="CenterScreen" Padding="0"
ui:EllipseClipper.ClipReferObject="{Binding ElementName=toolbarImage}" Topmost="True"
ui:EllipseClipper.IsWinClip="{Binding IsClip}" BorderThickness="1" BorderBrush="LightGray" ShowInTaskbar="False">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding WinInitCmd}"
CommandParameter="{Binding ElementName=mainWin}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<Window.Resources>
<ResourceDictionary Source="..\UI\MainDict.xaml"></ResourceDictionary>
</Window.Resources>
<Window.DataContext>
<vm:MainVm></vm:MainVm>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="55"/>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="*"/>
<RowDefinition Height="40"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Height="25" Orientation="Horizontal" Grid.Row="1" VerticalAlignment="Center" Margin="10 0 0 0">
<TextBlock Text="字体:" VerticalAlignment="Center" Margin="0 0 3 0"></TextBlock>
<ComboBox ItemsSource="{Binding FontFamilyCollection,UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding FontFamily,UpdateSourceTrigger=PropertyChanged}"
Width="180" Margin="0 0 10 0"></ComboBox>
<TextBlock Text="字号:" VerticalAlignment="Center" Margin="0 0 3 0"></TextBlock>
<ComboBox ItemsSource="{Binding FontSizeCollection,UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding FontSize,UpdateSourceTrigger=PropertyChanged}"
Width="100" Margin="0 0 10 0"></ComboBox>
<TextBlock Text="代码样式:" VerticalAlignment="Center" Margin="0 0 3 0"></TextBlock>
<ComboBox ItemsSource="{Binding CodeStyleCollection,UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding CodeStyle,UpdateSourceTrigger=PropertyChanged}"
Width="100" Margin="0 0 10 0"></ComboBox>
<TextBlock Text="编程语言:" VerticalAlignment="Center" Margin="0 0 3 0"></TextBlock>
<ComboBox ItemsSource="{Binding CodeLanguageCollection,UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding CodeLanguage,UpdateSourceTrigger=PropertyChanged}"
Width="100" Margin="0 0 10 0"></ComboBox>
<TextBlock Text="代码行号:" VerticalAlignment="Center" Margin="0 0 3 0"></TextBlock>
<CheckBox IsChecked="{Binding GutterStatus,UpdateSourceTrigger=PropertyChanged}" Grid.Row="3"
HorizontalAlignment="Left" VerticalAlignment="Center" Foreground="#8a8a8a" Padding="0">
<CheckBox.LayoutTransform>
<ScaleTransform ScaleX="1.5" ScaleY="1.5" />
</CheckBox.LayoutTransform>
</CheckBox>
</StackPanel>
<GroupBox Grid.Row="2" Header="预览窗口" Margin="10 0 10 0">
<cefSharp:ChromiumWebBrowser Grid.Row="2" MenuHandler="{StaticResource menuHandler}" x:Name="ChromBrower"
/>
</GroupBox>
<!--标题栏-->
<Grid Background="#e7eaed">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseMove">
<vb:YIEventToCommand Command="{Binding WindowMoveCmd}"
PassToEventArgs="True"></vb:YIEventToCommand>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="55"></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Margin="5" x:Name="toolbarImage" HorizontalAlignment="Center" VerticalAlignment="Center"
Source="..\Resources\YI64.png" Stretch="Fill" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding TitleImageUpCmd}" CommandParameter="{Binding ElementName=toolbarImage}"></i:InvokeCommandAction>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding TitleImageDownCmd}" CommandParameter="{Binding ElementName=toolbarImage}"></i:InvokeCommandAction>
</i:EventTrigger>
<i:EventTrigger EventName="MouseEnter">
<i:InvokeCommandAction Command="{Binding TitleImageEnterCmd}" CommandParameter="{Binding ElementName=toolbarImage}"></i:InvokeCommandAction>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeave">
<i:InvokeCommandAction Command="{Binding TitleImageLeaveCmd}" CommandParameter="{Binding ElementName=toolbarImage}"></i:InvokeCommandAction>
</i:EventTrigger>
<i:EventTrigger EventName="MouseRightButtonDown">
<i:InvokeCommandAction Command="{Binding TitleImageRightDownCmd}" CommandParameter="{Binding ElementName=toolbarImage}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Image>
<TextBlock Grid.Column="1" FontSize="20" Text="代码高亮软件" VerticalAlignment="Center" HorizontalAlignment="Left" Foreground="#4c4c4c"></TextBlock>
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Grid.Column="2">
<Button Visibility="{Binding PinVisiblity, UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource reverseVisiblityConverter}}"
ToolTip="将窗体固定在最前"
Style="{StaticResource toolbarBtnStyle}" Command="{Binding PinCmd}">
<Image Source="..\Resources\窗口非置顶状态.png" Stretch="Uniform" Height="20" Width="20"></Image>
</Button>
<Button Visibility="{Binding PinVisiblity,UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource toolbarBtnStyle}" Command="{Binding UnpinCmd}"
ToolTip="解除窗口置顶状态">
<Image Source="..\Resources\窗口置顶状态.png" Stretch="Uniform" Height="20" Width="20"></Image>
</Button>
<Button Command="{Binding MinimizedCmd}" Style="{StaticResource toolbarBtnStyle}">
<Image Source="..\Resources\最小化.png" Stretch="Uniform" Height="20" Width="20"></Image>
</Button>
<Button Visibility="{Binding NormalVisiblity,UpdateSourceTrigger=PropertyChanged,
Converter={StaticResource reverseVisiblityConverter}}"
Command="{Binding MaximizedCmd}" Style="{StaticResource toolbarBtnStyle}">
<Image Source="..\Resources\最大化.png" Stretch="Uniform" Height="20" Width="20"></Image>
</Button>
<Button Visibility="{Binding NormalVisiblity,UpdateSourceTrigger=PropertyChanged}"
Command="{Binding NormalCmd}" Style="{StaticResource toolbarBtnStyle}">
<Image Source="..\Resources\缩小.png" Stretch="Uniform" Height="20" Width="20"></Image>
</Button>
<Button Command="{Binding CloseCmd}" Style="{StaticResource toolbarBtnStyle}">
<Image Source="..\Resources\关闭.png" Stretch="Uniform" Height="20" Width="20"></Image>
</Button>
</WrapPanel>
</Grid>
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0 0 10 0">
<Button Template="{StaticResource ButtonTemplate}" Command="{Binding PasteCmd}" Content="粘贴" Width="70" Height="23"></Button>
<Button Template="{StaticResource ButtonTemplate}" Command="{Binding CopyCmd}" Margin="10 0 0 0" Content="复制" Width="70" Height="23"></Button>
</StackPanel>
</Grid>
</Window>
静态网页内容:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script type="text/javascript" src="scripts/shCore.js"></script>
<script id="codeTypeJs" type="text/javascript" src="scripts/shBrushPhp.js"></script>
<link id="css" type="text/css" rel="stylesheet" href="styles/shCoreDefault.css" />
<script type="text/javascript">
// 是否开启行号
function SwitchGutter(isOpen) {
//行号
SyntaxHighlighter.defaults['gutter'] = isOpen;
}
//更改代码样式
function ChangStyleType(type) {
var css = document.getElementById("css");
if ("Default" == type)
css.setAttribute("href", "styles/shCoreDefault.css");
if ("Emacs" == type)
css.setAttribute("href", "styles/shCoreEmacs.css");
if ("Django" == type)
css.setAttribute("href", "styles/shCoreDjango.css");
if ("Eclipse" == type)
css.setAttribute("href", "styles/shCoreEclipse.css");
if ("FadeToGrey" == type)
css.setAttribute("href", "styles/shCoreFadeToGrey.css");
if ("MDUltra" == type)
css.setAttribute("href", "styles/shCoreMDUltra.css");
if ("Midnight" == type)
css.setAttribute("href", "styles/shCoreMidnight.css");
if ("RDark" == type)
css.setAttribute("href", "styles/shCoreRDark.css");
}
//更改字号、字体
function ChangeFont(fontFamily, fontSize) {
var styleContent = '.syntaxhighlighter a,';
styleContent = styleContent + '.syntaxhighlighter div,';
styleContent = styleContent + '.syntaxhighlighter code,';
styleContent = styleContent + '.syntaxhighlighter table,';
styleContent = styleContent + '.syntaxhighlighter table td,';
styleContent = styleContent + '.syntaxhighlighter table tr,';
styleContent = styleContent + '.syntaxhighlighter table tbody,';
styleContent = styleContent + '.syntaxhighlighter table thead,';
styleContent = styleContent + '.syntaxhighlighter table caption,';
styleContent = styleContent + '.syntaxhighlighter textarea';
styleContent = styleContent + '{';
styleContent = styleContent + 'font-family:' + fontFamily + "!important;";
styleContent = styleContent + 'font-size:' + fontSize + 'px !important;';
styleContent = styleContent + 'line-height:1.2em !important;';
styleContent = styleContent + "}";
var fontStyle = document.createElement('style');
fontStyle.type = 'text/css';
document.getElementsByTagName('head')[0].appendChild(fontStyle);
fontStyle.appendChild(document.createTextNode(styleContent))
}
function InitSetting() {
//工具栏
SyntaxHighlighter.defaults['toolbar'] = false;
//滚动条
var initstyle = document.createElement('style');
initstyle.innerHTML = 'body::-webkit-scrollbar{width:0 !important;background:transparent}';
document.head.appendChild(initstyle);
}
function HeightLight(codeStyleType,fontFamily,fontSize,gutterStatus)
{
//更改样式
ChangStyleType(codeStyleType);
//更改字体、字号
ChangeFont(fontFamily, fontSize);
SwitchGutter(gutterStatus);
InitSetting();
}
window.onload = function () {
//CShape代碼調用方法
HeightLight('Default','微软雅黑',30,false);
}
SyntaxHighlighter.all();
// SyntaxHighlighter.all();
</script>
</head>
<body>
<pre id="code_pre" class="brush:Php">
/* ---示例代码----*/
function echo (){
var a="this is a example";
alert("hello world "+a);
}
/* ---示例代码----*/
</pre>
</body>
</html>
最终实现效果
右键在YI图标上点击可缩小为图标,在缩小的图标上左键点击则完成粘贴加复制的功能。
以上是部分实现的代码,具体代码见github:代码高亮软件
懒的编译的可下载安装包:代码高亮软件安装包
最后说一句,由于SyntaxHighlighter渲染的网页是以表格排布的,所以代码高亮结果复制到Word是不生效的。
才疏学浅,望大家多多指点!