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>

DoAppend メソッドによって生成されるテキストに TextBlock をバインドするだけでなく、TextBlock の Text プロパティを設定するだけで無駄にしようとしました。

小さいながらも大きなポイントがいくつか欠けていると確信していますが、今日の午後、約 3 時間頭をかきむしりましたが、役に立ちませんでした。

どんな助けでも大歓迎です!!

ありがとうブライアン

4

2 に答える 2

1

ここの log4net Appenderが役立つかもしれません。要するに、Pete は INotifyPropertyChanged をサポートする lognet Appender を実装し、これまでに記録されたすべてのメッセージを連結したプロパティを公開します。上記でリンクしたページのリンクから入手できるサンプル プログラムは、ビューを Appender の出力にバインドする方法を示しています。私は実際にそれを使用していないので、それがどれほど有用か (またはそうでないか) についてコメントすることはできませんが、解決しようとしているのと同じ問題を解決しようとしているようです。

幸運を!

于 2013-01-04T16:20:38.807 に答える
0

残念ながら、この特定の問題に対する迅速な解決策を見つけることができませんでした。log4netログをWPFフォームに取得するという問題を解決するために私がしたことは、ちょっとしたハックを作成することでした(またはそうではない??)。

log4net の「ログ」テキストを一度に 1 行ずつ公開するシングルトンを作成しました (多くのメモリを使いたくありませんでした)。基本的に、SingletonAppender には「Log」という 1 つのプロパティがあり、以下に示すカスタム 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
}

プロセス中に発生しているログ イベントを取得するために、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.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 に答える