簡體   English   中英

Backgroundworker在ui上更新日志

[英]Backgroundworker updating log on the ui

我有一個wpf應用程序,該應用程序在主窗口中帶有一個文本框,該文本框應該用於在用戶運行較長時間時顯示日志記錄信息。

<TextBox Grid.Row="1" Margin="10,10,10,10" AcceptsReturn="True" Name="txtLogging" TextWrapping="WrapWithOverflow" 
                 Text="{Binding Path=LogText, Mode=TwoWay}" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto" />

public string LogText
{
    get { return _logText; }
    set
    {
        _logText = value;
        OnPropertyChanged();
    }
}

ui上的按鈕之一將啟動一個過程,該過程至少需要30秒,有時甚至需要幾個小時。 不用說,最好在后台工作人員上運行它。 問題是程序中的日志記錄類是在UI線程上創建的,必須在工作程序執行期間進行訪問,以使用當前發生的日志更新UI。

記錄器看起來像這樣;

using System;
using System.IO;

namespace BatchInvoice
{
    public enum LoggingLevel
    {
        Verbose = 0,
        Info = 1,
        Warning = 2,
        Error = 3
    }
    public sealed class Logger
    {

        string _logFile;
        static Logger() { }
        public bool LogToDataBase = false;
        public bool LogToFile = true;
        public bool LogToScreen = false;
        private Logger()
        {
            //string filePath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            string filePath = Directory.GetCurrentDirectory();
            filePath = filePath + @"\LogFiles";
            string extension = ".log";
            if (!Directory.Exists(filePath))
            {
                Directory.CreateDirectory(filePath);
            }
            /*string currentDir = Environment.CurrentDirectory;
            DirectoryInfo directory = new DirectoryInfo(currentDir);
            string fullDirectory = directory.FullName;*/
            string date = (DateTime.Now).ToString("yyyyMMddHHmmss");
            _logFile = filePath + "\\" + date + extension;
            minimumLoggingLevel = LoggingLevel.Info;
        }
        private LoggingLevel minimumLoggingLevel;
        public static void SetMinimumLoggingLevel(LoggingLevel minimum)
        {
            Instance.minimumLoggingLevel = minimum;
        }
        public static LoggingLevel GetMinimumLoggingLevel()
        {
            return Instance.minimumLoggingLevel;
        }
        private static readonly Logger instance = new Logger();
        public static Logger Instance
        {
            get
            {
                return instance;
            }
        }
        public static void Write(string content)
        {
            using (StreamWriter fileWriter = File.AppendText(Instance._logFile))
            {
                fileWriter.WriteLine(content);
            }
        }
        public static void Write(string content, LoggingLevel warningLevel)
        {
            if (Instance.minimumLoggingLevel <= warningLevel)
            {
                if (Instance.LogToFile)
                {
                    using (StreamWriter fileWriter = File.AppendText(Instance._logFile))
                    {
                        fileWriter.WriteLine(warningLevel.ToString() + ": " + content);
                    }
                }
                if (Instance.LogToScreen)
                    ScreenLogging.Write(content, warningLevel);
                if (Instance.LogToDataBase)
                {
                    //enter database loggign code here.
                }
            }
        }
    }
}

using System.Windows;
using System.Windows.Controls;

namespace BatchInvoice
{
    public class ScreenLogging
    {
        private static ScreenLogging _instance;
        private ScreenLogging() { }
        public static ScreenLogging Instance
        {
            get
            {
                if(_instance == null)
                {
                    _instance = new ScreenLogging();
                }
                return _instance;
            }
        }
        private TextBox _target;
        public static void SetTarget(TextBox target)
        {
            Instance._target = target;
        }
        public static void Write(string content, LoggingLevel warningLevel)
        {
            //MessageBox.Show(content, warningLevel.ToString());
            Instance._target.AppendText(warningLevel.ToString() + ": " + content + "\n");
        }
    }
}

(是的,有一個原因將屏幕記錄分為一個不同的類,但是我真的希望我不必對此進行更改。)如何使該記錄類的調用從后台工作程序中反映到UI上? 我是否應該更改LogText屬性以從外部文件或類似的東西讀取? 當前,我沒有實現后台工作程序,因此日志僅在任務完成后顯示,但是我需要能夠在其運行時監視其進度。 當我嘗試將其放入后台工作程序時,它碰到試圖訪問記錄器的一行代碼時出錯。

由於您嘗試從另一個線程更新UI,因此必須以一種特殊的方式進行,即必須同步線程以在它們之間傳輸數據。 換句話說,就像BackgroundWorker需要暫停以更新UI一樣。 可以使用BackgroundWorker的ProgressChanged事件和ReportProgress方法來完成。 這是一個簡單的示例:

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        // I guess this is how you are using your logger, right?
        ScreenLogging.SetTarget(this.txtLogging);

        BackgroundWorker worker = new BackgroundWorker();

        // Your classic event to do the background work...
        worker.DoWork += Worker_DoWork;

        // Here you can sender messages to UI.
        worker.ProgressChanged += Worker_ProgressChanged;

        // Don't forget to turn this property to true.
        worker.WorkerReportsProgress = true;

        worker.RunWorkerAsync();
    }

    private void Worker_DoWork(object sender, DoWorkEventArgs e)
    {
        var worker = sender as BackgroundWorker;

        Thread.Sleep(3000);

        // ReportProgress sends two values to the ProgressChanged method, for the
        // ProgressChangedEventArgs object. The first one is the percentage of the 
        // work, and the second one can be any object that you need to pass to UI.
        // In a simple example, I am passing my log message and just putting 
        // any random value at progress, since it does not matter here.
        worker.ReportProgress(0, "Test!");
    }

    private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        // Here you get your UserState object, wich is my string message passed on 
        // with the ReportProgress method above.
        var message = e.UserState as string;

        // Then you call your log as always. Simple, right?
        ScreenLogging.Write(message, LoggingLevel.Info);
    }

由於您的問題似乎無法重寫所有日志調用,因此我將發布另一種方法,只需更改ScreenLogging.Write方法即可。 我希望它對您有用,因為您無需更改對Logger.Write方法的調用。

public class ScreenLogging
{
    private static ScreenLogging _instance;
    private ScreenLogging() { }
    public static ScreenLogging Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new ScreenLogging();
            }
            return _instance;
        }
    }
    private TextBox _target;
    public static void SetTarget(TextBox target)
    {
        Instance._target = target;
    }
    public static void Write(string content, LoggingLevel warningLevel)
    {
        var appendTextAction = new Action(() =>
        {
            var text = warningLevel.ToString() + ": " + content + "\n";
            Instance._target.AppendText(text);
        });

        // Only the thread that the Dispatcher was created on may access the
        // DispatcherObject directly. To access a DispatcherObject from a 
        // thread other than the thread the DispatcherObject was created on,
        // call Invoke and BeginInvoke on the Dispatcher the DispatcherObject 
        // is associated with.
        // You can set the priority to Background, so you guarantee that your
        // key operations will be processed first, and the screen updating 
        // operations will happen only after those operations are done.
        Instance._target.Dispatcher.Invoke(appendTextAction, 
            DispatcherPriority.Background);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM