Wpf NLog 显示日志到 RichTextBox

发布时间 2023-12-21 11:01:09作者: 无处不在-超超

1. 项目中引入库

  NLog

2. 引入三个文件:

  WpfRichTextBoxTarget.cs

  1 // 
  2 // Copyright (c) 2004-2011 Jaroslaw Kowalski <jaak@jkowalski.net>
  3 // 
  4 // All rights reserved.
  5 // 
  6 // Redistribution and use in source and binary forms, with or without 
  7 // modification, are permitted provided that the following conditions 
  8 // are met:
  9 // 
 10 // * Redistributions of source code must retain the above copyright notice, 
 11 //   this list of conditions and the following disclaimer. 
 12 // 
 13 // * Redistributions in binary form must reproduce the above copyright notice,
 14 //   this list of conditions and the following disclaimer in the documentation
 15 //   and/or other materials provided with the distribution. 
 16 // 
 17 // * Neither the name of Jaroslaw Kowalski nor the names of its 
 18 //   contributors may be used to endorse or promote products derived from this
 19 //   software without specific prior written permission. 
 20 // 
 21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 25 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 26 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 27 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 28 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 29 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
 31 // THE POSSIBILITY OF SUCH DAMAGE.
 32 // 
 33 
 34 using System.Diagnostics;
 35 using System.Windows.Controls;
 36 using NLog;
 37 using NLog.Targets;
 38 
 39 #if !NET_CF && !MONO && !SILVERLIGHT
 40 
 41 namespace NLog.Targets.Helper
 42 {
 43     using System;
 44     using System.Collections.Generic;
 45     using System.Collections.ObjectModel;
 46     using System.ComponentModel;
 47     using System.Windows;
 48     using NLog.Config;
 49     using System.Windows.Documents;
 50     using System.Windows.Media;
 51     using System.Linq;
 52     [Target("RichTextBox")]
 53     public sealed class WpfRichTextBoxTarget : TargetWithLayout
 54     {
 55         private int _width = 500;
 56         private int _height = 500;
 57         private static readonly TypeConverter colorConverter = new ColorConverter();
 58 
 59         static WpfRichTextBoxTarget()
 60         {
 61             var rules = new List<WpfRichTextBoxRowColoringRule>()
 62             {
 63                 new WpfRichTextBoxRowColoringRule("level == LogLevel.Fatal", "White", "Red", FontStyles.Normal, FontWeights.Bold),
 64                 new WpfRichTextBoxRowColoringRule("level == LogLevel.Error", "Red", "Empty", FontStyles.Italic, FontWeights.Bold),
 65                 new WpfRichTextBoxRowColoringRule("level == LogLevel.Warn", "Orange", "Empty"),
 66                 new WpfRichTextBoxRowColoringRule("level == LogLevel.Info", "Black", "Empty"),
 67                 new WpfRichTextBoxRowColoringRule("level == LogLevel.Debug", "Gray", "Empty"),
 68                 new WpfRichTextBoxRowColoringRule("level == LogLevel.Trace", "DarkGray", "Empty", FontStyles.Italic, FontWeights.Normal),
 69             };
 70 
 71             DefaultRowColoringRules = rules.AsReadOnly();
 72         }
 73 
 74         public WpfRichTextBoxTarget()
 75         {
 76             WordColoringRules = new List<WpfRichTextBoxWordColoringRule>();
 77             RowColoringRules = new List<WpfRichTextBoxRowColoringRule>();
 78             ToolWindow = true;
 79         }
 80 
 81         private delegate void DelSendTheMessageToRichTextBox(string logMessage, WpfRichTextBoxRowColoringRule rule);
 82 
 83         private delegate void FormCloseDelegate();
 84 
 85         public static ReadOnlyCollection<WpfRichTextBoxRowColoringRule> DefaultRowColoringRules { get; private set; }
 86 
 87         public string ControlName { get; set; }
 88 
 89         public string FormName { get; set; }
 90 
 91         [DefaultValue(false)]
 92         public bool UseDefaultRowColoringRules { get; set; }
 93 
 94         [ArrayParameter(typeof(WpfRichTextBoxRowColoringRule), "row-coloring")]
 95         public IList<WpfRichTextBoxRowColoringRule> RowColoringRules { get; private set; }
 96 
 97         [ArrayParameter(typeof(WpfRichTextBoxWordColoringRule), "word-coloring")]
 98         public IList<WpfRichTextBoxWordColoringRule> WordColoringRules { get; private set; }
 99 
100         [DefaultValue(true)]
101         public bool ToolWindow { get; set; }
102 
103         public bool ShowMinimized { get; set; }
104 
105         public int Width
106         {
107             get { return _width; }
108             set { _width = value; }
109         }
110 
111         public int Height
112         {
113             get { return _height; }
114             set { _height = value; }
115         }
116 
117         public bool AutoScroll { get; set; }
118 
119         public int MaxLines { get; set; }
120 
121         internal Window TargetForm { get; set; }
122 
123         internal System.Windows.Controls.RichTextBox TargetRichTextBox { get; set; }
124 
125         internal bool CreatedForm { get; set; }
126 
127         protected override void InitializeTarget()
128         {
129             TargetRichTextBox = TargetRichTextBox ?? System.Windows.Application.Current.MainWindow.FindName(ControlName) as System.Windows.Controls.RichTextBox;
130 
131             if (TargetRichTextBox != null) return;
132             //this.TargetForm = FormHelper.CreateForm(this.FormName, this.Width, this.Height, false, this.ShowMinimized, this.ToolWindow);
133             //this.CreatedForm = true;
134 
135             var openFormByName = System.Windows.Application.Current.Windows.Cast<Window>().FirstOrDefault(x => x.GetType().Name == FormName);
136             if (openFormByName != null)
137             {
138                 TargetForm = openFormByName;
139                 if (string.IsNullOrEmpty(ControlName))
140                 {
141                     // throw new NLogConfigurationException("Rich text box control name must be specified for " + GetType().Name + ".");
142                     Trace.WriteLine("Rich text box control name must be specified for " + GetType().Name + ".");
143                 }
144 
145                 CreatedForm = false;
146                 TargetRichTextBox = TargetForm.FindName(ControlName) as System.Windows.Controls.RichTextBox;
147 
148                 if (TargetRichTextBox == null)
149                 {
150                     // throw new NLogConfigurationException("Rich text box control '" + ControlName + "' cannot be found on form '" + FormName + "'.");
151                     Trace.WriteLine("Rich text box control '" + ControlName + "' cannot be found on form '" + FormName + "'.");
152                 }
153             }
154 
155             if (TargetRichTextBox == null)
156             {
157                 TargetForm = new Window
158                 {
159                     Name = FormName,
160                     Width = Width,
161                     Height = Height,
162                     WindowStyle = ToolWindow ? WindowStyle.ToolWindow : WindowStyle.None,
163                     WindowState = ShowMinimized ? WindowState.Minimized : WindowState.Normal,
164                     Title = "NLog Messages"
165                 };
166                 TargetForm.Show();
167 
168                 TargetRichTextBox = new System.Windows.Controls.RichTextBox { Name = ControlName };
169                 var style = new Style(typeof(Paragraph));
170                 TargetRichTextBox.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
171                 style.Setters.Add(new Setter(Block.MarginProperty, new Thickness(0, 0, 0, 0)));
172                 TargetRichTextBox.Resources.Add(typeof(Paragraph), style);
173                 TargetForm.Content = TargetRichTextBox;
174 
175                 CreatedForm = true;
176             }
177         }
178 
179         protected override void CloseTarget()
180         {
181             if (CreatedForm)
182             {
183                 try
184                 {
185                     TargetForm.Dispatcher.Invoke(() =>
186                     {
187                         TargetForm.Close();
188                         TargetForm = null;
189                     });
190                 }
191                 catch
192                 {
193                 }
194 
195 
196 
197             }
198         }
199 
200         protected override void Write(LogEventInfo logEvent)
201         {
202             WpfRichTextBoxRowColoringRule matchingRule = RowColoringRules.FirstOrDefault(rr => rr.CheckCondition(logEvent));
203 
204             if (UseDefaultRowColoringRules && matchingRule == null)
205             {
206                 foreach (var rr in DefaultRowColoringRules.Where(rr => rr.CheckCondition(logEvent)))
207                 {
208                     matchingRule = rr;
209                     break;
210                 }
211             }
212 
213             if (matchingRule == null)
214             {
215                 matchingRule = WpfRichTextBoxRowColoringRule.Default;
216             }
217 
218             var logMessage = Layout.Render(logEvent);
219 
220             if (System.Windows.Application.Current == null) return;
221 
222             try
223             {
224                 if (System.Windows.Application.Current.Dispatcher.CheckAccess() == false)
225                 {
226                     System.Windows.Application.Current.Dispatcher.Invoke(() => SendTheMessageToRichTextBox(logMessage, matchingRule));
227                 }
228                 else
229                 {
230                     SendTheMessageToRichTextBox(logMessage, matchingRule);
231                 }
232             }
233             catch (Exception ex)
234             {
235                 Debug.WriteLine(ex);
236             }
237 
238         }
239 
240 
241         private static Color GetColorFromString(string color, Brush defaultColor)
242         {
243 
244             if (color == "Empty")
245             {
246                 color = "White";
247             }
248 
249             return (Color)colorConverter.ConvertFromString(color);
250         }
251 
252 
253         private void SendTheMessageToRichTextBox(string logMessage, WpfRichTextBoxRowColoringRule rule)
254         {
255             System.Windows.Controls.RichTextBox rtbx = TargetRichTextBox;
256 
257             var scrolledToEnd =
258                 AutoScroll
259                 && (TargetRichTextBox.VerticalOffset + TargetRichTextBox.ViewportHeight) >= (TargetRichTextBox.ExtentHeight - .1);
260 
261             var tr = new TextRange(rtbx.Document.ContentEnd, rtbx.Document.ContentEnd);
262             tr.Text = logMessage + "\n";
263             tr.ApplyPropertyValue(TextElement.ForegroundProperty,
264                 new SolidColorBrush(GetColorFromString(rule.FontColor, (Brush)tr.GetPropertyValue(TextElement.ForegroundProperty)))
265             );
266             tr.ApplyPropertyValue(TextElement.BackgroundProperty,
267                 new SolidColorBrush(GetColorFromString(rule.BackgroundColor, (Brush)tr.GetPropertyValue(TextElement.BackgroundProperty)))
268             );
269             tr.ApplyPropertyValue(TextElement.FontStyleProperty, rule.Style);
270             tr.ApplyPropertyValue(TextElement.FontWeightProperty, rule.Weight);
271 
272             if (MaxLines > 0)
273             {
274                 while (rtbx.Document.Blocks.Count - 1 > MaxLines)
275                 {
276                     rtbx.Document.Blocks.Remove(rtbx.Document.Blocks.FirstBlock);
277                 }
278             }
279 
280             if (AutoScroll && scrolledToEnd)
281             {
282                 rtbx.ScrollToEnd();
283             }
284         }
285     }
286 }
287 #endif
View Code

  WpfRichTextBoxRowColoringRule.cs

 1 // 
 2 // Copyright (c) 2004-2011 Jaroslaw Kowalski <jaak@jkowalski.net>
 3 // 
 4 // All rights reserved.
 5 // 
 6 // Redistribution and use in source and binary forms, with or without 
 7 // modification, are permitted provided that the following conditions 
 8 // are met:
 9 // 
10 // * Redistributions of source code must retain the above copyright notice, 
11 //   this list of conditions and the following disclaimer. 
12 // 
13 // * Redistributions in binary form must reproduce the above copyright notice,
14 //   this list of conditions and the following disclaimer in the documentation
15 //   and/or other materials provided with the distribution. 
16 // 
17 // * Neither the name of Jaroslaw Kowalski nor the names of its 
18 //   contributors may be used to endorse or promote products derived from this
19 //   software without specific prior written permission. 
20 // 
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
25 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
26 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
28 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
29 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
31 // THE POSSIBILITY OF SUCH DAMAGE.
32 // 
33 
34 using NLog;
35 
36 #if !NET_CF && !MONO && !SILVERLIGHT
37 
38 namespace NLog.Targets .Helper
39 {
40     using System.ComponentModel;
41     using System.Windows;
42     using NLog.Conditions;
43     using NLog.Config;
44 
45    [NLogConfigurationItem]
46     public class WpfRichTextBoxRowColoringRule
47     {
48         static WpfRichTextBoxRowColoringRule()
49         {
50             Default = new WpfRichTextBoxRowColoringRule();
51         }
52 
53         public WpfRichTextBoxRowColoringRule()
54             : this(null, "Empty", "Empty", FontStyles.Normal, FontWeights.Normal)
55         {
56         }
57 
58         public WpfRichTextBoxRowColoringRule(string condition, string fontColor, string backColor, FontStyle fontStyle, FontWeight fontWeight)
59         {
60             Condition = condition;
61             FontColor = fontColor;
62             BackgroundColor = backColor;
63             Style = fontStyle;
64             Weight = fontWeight;
65         }
66 
67         public WpfRichTextBoxRowColoringRule(string condition, string fontColor, string backColor)
68         {
69             Condition = condition;
70             FontColor = fontColor;
71             BackgroundColor = backColor;
72             Style = FontStyles.Normal;
73             Weight = FontWeights.Normal;
74         }
75 
76         public static WpfRichTextBoxRowColoringRule Default { get; private set; }
77 
78         [RequiredParameter]
79         public ConditionExpression Condition { get; set; }
80 
81         [DefaultValue("Empty")]
82         public string FontColor { get; set; }
83 
84         [DefaultValue("Empty")]
85         public string BackgroundColor { get; set; }
86 
87         public FontStyle Style { get; set; }
88 
89         public FontWeight Weight { get; set; }
90 
91         public bool CheckCondition(LogEventInfo logEvent)
92         {
93             return true.Equals(Condition.Evaluate(logEvent));
94         }
95     }
96 }
97 #endif
View Code

  WpfRichTextBoxWordColoringRule.cs

// 
// Copyright (c) 2004-2011 Jaroslaw Kowalski <jaak@jkowalski.net>
// 
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions 
// are met:
// 
// * Redistributions of source code must retain the above copyright notice, 
//   this list of conditions and the following disclaimer. 
// 
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution. 
// 
// * Neither the name of Jaroslaw Kowalski nor the names of its 
//   contributors may be used to endorse or promote products derived from this
//   software without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
// THE POSSIBILITY OF SUCH DAMAGE.
// 

using System.ComponentModel;
using System.Text.RegularExpressions;
using System.Windows;
using NLog.Config;

#if !NET_CF && !MONO && !SILVERLIGHT

namespace NLog.Targets .Helper
{
    [NLogConfigurationItem]
    public class WpfRichTextBoxWordColoringRule
    {
        private Regex compiledRegex;

        public WpfRichTextBoxWordColoringRule()
        {
            FontColor = "Empty";
            BackgroundColor = "Empty";
        }

        public WpfRichTextBoxWordColoringRule(string text, string fontColor, string backgroundColor)
        {
            Text = text;
            FontColor = fontColor;
            BackgroundColor = backgroundColor;
            Style = FontStyles.Normal;
            Weight = FontWeights.Normal;
        }

        public WpfRichTextBoxWordColoringRule(string text, string textColor, string backgroundColor, FontStyle fontStyle, FontWeight fontWeight)
        {
            Text = text;
            FontColor = textColor;
            BackgroundColor = backgroundColor;
            Style = fontStyle;
            Weight = fontWeight;
        }

        public string Regex { get; set; }

        public string Text { get; set; }

        [DefaultValue(false)]
        public bool WholeWords { get; set; }

        [DefaultValue(false)]
        public bool IgnoreCase { get; set; }

        public FontStyle Style { get; set; }

        public FontWeight Weight { get; set; }

        public Regex CompiledRegex
        {
            get
            {
                if (compiledRegex == null)
                {
                    string regexpression = Regex;
                    if (regexpression == null && Text != null)
                    {
                        regexpression = System.Text.RegularExpressions.Regex.Escape(Text);
                        if (WholeWords)
                        {
                            regexpression = "\b" + regexpression + "\b";
                        }
                    }

                    RegexOptions regexOptions = RegexOptions.Compiled;
                    if (IgnoreCase)
                    {
                        regexOptions |= RegexOptions.IgnoreCase;
                    }

                    compiledRegex = new Regex(regexpression, regexOptions);
                }

                return compiledRegex;
            }
        }

        [DefaultValue("Empty")]
        public string FontColor { get; set; }

        [DefaultValue("Empty")]
        public string BackgroundColor { get; set; }
    }
}
#endif
View Code

 

3. xaml 定义控件

 1         <RichTextBox
 2             x:Name="loggerTextBox"
 3             Grid.RowSpan="5"
 4             Grid.Column="1"
 5             Grid.ColumnSpan="2"
 6             Margin="5"
 7             HorizontalScrollBarVisibility="Visible"
 8             VerticalScrollBarVisibility="Visible">
 9             <!--<FlowDocument PageWidth="2000">
10                 <Paragraph
11                     Margin="0"
12                     FontFamily="Lucida Console"
13                     FontSize="11" />
14             </FlowDocument>-->
15         </RichTextBox>

4. 初始化Logger

static public Logger? logger;

 1 private void Window_Loaded(object sender, RoutedEventArgs e)
 2 {
 3    InitUILogging(LogLevel.Info);
 4    logger = LogManager.GetCurrentClassLogger();
 5 }
 6 private void InitUILogging(LogLevel logLevel)
 7 {
 8     // log layout format
 9     //var layout = "${message} ${exception:separator=\r\n:format=message,type,method,stackTrace:maxInnerExceptionLevel=10:innerExceptionSeparator=\r\n:innerFormat=message,type,method,stackTrace}";
10     //var layout = "${date:format=HH\\:MM\\:ss} ${logger} ${message}";
11     //var layout = "${time:}|${threadid:padding=3}|${level:uppercase=true:padding=-5}|${logger:padding=-15}|${message}|${exception}";
12     var layout = "${time:}|${threadid:padding=3}|${level:uppercase=true:padding=-5}|${message}|${exception}";
13     // create rich text box target
14     var uiTarget = new WpfRichTextBoxTarget
15     {
16         Layout = layout,
17         TargetRichTextBox = loggerTextBox,
18         UseDefaultRowColoringRules = true,
19         AutoScroll = true,
20         MaxLines = 250,
21     };
22     var asyncWrapper = new AsyncTargetWrapper
23     {
24         Name = "logEidt",
25         WrappedTarget = uiTarget
26     };
27     var config = LogManager.Configuration ?? new LoggingConfiguration();
28 
29     config.AddTarget("UI", uiTarget);
30     config.LoggingRules.Add(new LoggingRule("*", logLevel, asyncWrapper));
31 
32     LogManager.Configuration = config;
33 }

 

5. 输出日志

1 MainWindow.logger?.Info(msg);

 

参考来源:

WpfRichTextBoxTarget, NLog.Targets C# (CSharp) Code Examples - HotExamples

BitSharp/BitSharp.Client at master · hoangduit/BitSharp (github.com)