1

我想知道是否有人遇到过这个问题。我试图在 WPF 应用程序的 RichTextBox 中显示所有日志记录事件,并认为我可以使用 UserControl 上的 IAppender 接口来添加全局 Appender。

我正在使用 BackgroundWorker 在后台运行“长时间运行”进程,并在 DoWork 事件期间创建 logger.Info("text") 事件。

我遇到的问题是 DoAppend 事件触发,并且似乎文本在 Dispatcher 事件期间得到更新,但 UI 没有反映这一点。

这是我的 LogAppender 类:

public partial class LogAppender : UserControl, IAppender
{
    public LogAppender()
    {
        InitializeComponent();
    }

    public void Close()
    {
    }

    public log4net.Layout.PatternLayout Layout
    {
        get;
        set;
    }

    public void DoAppend(log4net.Core.LoggingEvent loggingEvent)
    {
        this.LogTextBlock.Dispatcher.Invoke(
            DispatcherPriority.Normal,
            new Action(
                delegate
                {
                    StringWriter writer = new StringWriter();

                    this.Layout.Format(writer, loggingEvent);

                    this.LogTextBlock.AppendText(writer.ToString());
                }));
    }
}

<UserControl>
    <Grid>
        <RichTextBox x:Name="LogTextBlock"/>
    </Grid>
</UserControl>

这是我的 MainWindow.xaml 按钮单击和 backgroundworker 代码:

public partial class MainWindow
{
    protected static readonly ILog logger = LogManager.GetLogger(typeof(MainWindow));

    public MainWindow()
    {
        InitializeComponent();
    }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        BackgroundWorker bw = new BackgroundWorker();
        bw.DoWork += bw_DoWork;
        bw.RunWorkerCompleted += bw_RunWorkerCompleted;
        bw.RunWorkerAsync();
    }

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        logger.Info("Complete");
    }

    void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        logger.Info("Start");
        Thread.Sleep(5000);
        logger.Info("End");
    }
}

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Button Height="30" Click="Button_Click_1">Start</Button>
    <local:LogAppender Grid.Row="1" Grid.ColumnSpan="2"/>
</Grid>

我尝试将 TextBlock 绑定到由 DoAppend 方法生成的文本以及设置 TextBlock 的 Text 属性,但无济于事。

我确定我遗漏了一些小而重要的观点,但是今天下午我挠了挠头大约 3 个小时,但无济于事。

任何帮助表示赞赏!

谢谢布莱恩

4

2 回答 2

1

这里的 log4net Appender可能会对您有所帮助。本质上,Pete 实现了一个支持 INotifyPropertyChanged 的​​ lognet Appender,并公开了一个属性,该属性是迄今为止记录的所有消息的串联。通过我上面链接的页面中的链接提供的示例程序显示了如何使您的视图绑定到 Appender 的输出。我实际上并没有使用过它,所以我无法评论它有多么有用(或没用),但它似乎确实在试图解决你试图解决的同一个问题。

祝你好运!

于 2013-01-04T16:20:38.807 回答
0

不幸的是,我无法找到解决这个特定问题的快速方法。我为解决将 log4net 日志转换为 WPF 表单的问题所做的工作是创建了一些 hack(或者可能不是??)。

我创建了一个单例,一次暴露一行 log4net“日志”文本(不想占用太多内存)。基本上,SingletonAppender 上有一个属性“Log”,它在自定义 IAppender 的 DoAppend 事件期间被更改或更新,如下所示。

public class SingletonAppender
{
    private static volatile SingletonAppender instance;

    private static object syncRoot = new Object();

    private string log = string.Empty;

    private SingletonAppender() 
    {

    }

    public static SingletonAppender Instance
    {
        get
        {
            if (instance == null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                    {
                        instance = new SingletonAppender();
                    }
                }
            }

            return instance;
        }
    }

    public string Log
    {
        get
        {
            string retValue = this.log;

            // whenever we "get" the Log property, reset the underlying value to empty
            this.log = string.Empty;

            return retValue;
        }

        set
        {
            this.log = value;
        }
    }
}

public class LogAppender : IAppender
{
    private string name = string.Empty;

    public LogAppender()
    {
    }

    #region IAppender Members

    public void Close()
    {
    }

    public void DoAppend(log4net.Core.LoggingEvent loggingEvent)
    {
        string retValue = string.Empty;

        if (this.Layout != null)
        {
            StringWriter writer = new StringWriter();
            this.Layout.Format(writer, loggingEvent);
            retValue = writer.ToString();
        }
        else
        {
            retValue = loggingEvent.MessageObject.ToString();
        }

        SingletonAppender.Instance.Log += retValue;
    }

    public log4net.Layout.PatternLayout Layout
    {
        get;
        set;
    }

    public string Name
    {
        get
        {
            return this.name;
        }
        set
        {
            this.name = value;
        }
    }

    #endregion
}

我在后台工作人员中创建了一个“心跳”计时器,以获取在此过程中发生的日志事件:

public partial class MainWindow
{
    protected static readonly ILog logger = LogManager.GetLogger(typeof(MainWindow));

    public MainWindow()
    {
        InitializeComponent();
    }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        BackgroundWorker bw = new BackgroundWorker();
        bw.DoWork += bw_DoWork;
        bw.ProgressChanged += bw_ProgressChanged;
        bw.RunWorkerCompleted += bw_RunWorkerCompleted;
        bw.RunWorkerAsync();
    }

    void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
         this.LogTextBox.AppendText(SingletonAppender.Instance.Log);
    }

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        logger.Info("Complete");
    }

    void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        logger.Info("Start");
        Timer timer = new Timer(new TimerCallback(TimerCallback), sender, 500, 2000);
        Thread.Sleep(5000);
        logger.Info("End");
    }

    public void TimerCallback(object state)
    {
        if (state != null)
        {
            BackgroundWorker worker = state as BackgroundWorker;

            if (worker != null && worker.IsBusy)
            {
                worker.ReportProgress(0, null);
            }
        }
    }
}

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Button Height="30" Click="Button_Click_1">Start</Button>
    <RichTextBox x:Name="LogTextBox"/>
</Grid>
于 2013-01-04T15:49:56.543 回答