#今、ちょっとログブラウザを作っています。
決まったディレクトリにあるログファイルの一覧を作って、そこから選んだらログファイルを閲覧できる。
まあ、それだけなら楽ちんなのですが、「特定のキーワードでは色付けしたい」「今見ている行がすぐわかるようにしたい」「キーボードで今見ている行を移動できるようにしたい」
ということを実現しようとしています。
当初は、Windows FormsでRichTextBox
を使うという方法を考えました。特定のキーワードで色付けはすでに実績があります。
簡単に言うと、
void LoadFile(string FileName, string KeyWordRegEx)
{
richTextBox1.Text = File.ReadAllText(FileName);
var ms = RegEx.Matches(richTextBox1.Text, KeyWordRegEx).Cast<Match>().Reverse;
foreach(var m in ms)
{
richTextBox1.Select(m.Index, m.Length);
richTextBox1.SelectionColor = Color.Red;
}
}
しかし、「今見ている行がすぐわかるようにしたい」の実現がなかなか難しい。
現在のカレット位置に合わせてPictureを動かすというかなり強引な手法で実装してみたのだけど、どうも位置がうまくあわない。
##若者よWindowsFormsを捨てよ
いろいろ悩んだ挙げ句に、Windows Formsを捨てて**WPF(Windows Presentation Foundation)**でちょっと試してみることにしました。
Windows FormsのRichTextBox
は上記のように読み込んだテキストファイルをRichTextBox.Text
に代入して、そこにRtf
を編集するか、RichTextBox
上で選択->カラー変更 を繰り返すなんて手法になります。Rtfの編集は早いのですが、かなりハードルは高いです。
WPFのRichTextBox
はぜんぜん違います。
簡単に言うと、FlowDocument
というオブジェクトをRichTextBox.Document
に割り付けます。
FlowDocument
オブジェクトの下にBlock
オブジェクトを起き、そのBlock
オブジェクトの中にさらに下位のオブジェクトを配置するなんていう迂遠なことをやります。なお、それぞれのオブジェクト単位でしか色づけとかはできません。
というわけで、
void LoadFile(FlowDocument fd, string Path)
{
var Lines = File.ReadLines(Path);
foreach(var Line in Lines)
{
var p = new Paragraph();
var ms = RegularExpressions.RegEx.Split(Line, Pattern);
foreach(var m in ms)
{
var b = new Run(m);
if (Pattern.Contains(m))
b.Foreground = Brushes.Pink;
p.Inlines.Add(b);
}
fd.Blocks.Add(p);
}
}
なんて形で書きます。ここで重要なのはPatternに書く正規表現です。例えば、「OK」「完了」という文字を色付けしたければ、
"(OK)|(完了)"
と書きます。
こうすると、RegEx.Split()
で分割する際に分割パターンも配列に入ります。
後は、RichTextBox
のSelectionChanged
イベントをトリガーにして、fd
以下のすべてのBlockオブジェクトのBackcolorを変更します。こんな感じですかね。
void CaretMoved( object sender, EventArgs e )
{
var Target = fd.CaretPosition.Paragraph;
foreach(var b in fd.Blocks.Where(p => p!=Target))
{
b.Background = Brushes.White;
}
if (Target != null) Target.Backcolor = Brushes.LightSkyBlue;
}
みたいな感じで書けば、カレットが動くたびにその行の色が反転してくれます。