1. 程式人生 > >WPF BackgroundWorker執行緒與進度的處理

WPF BackgroundWorker執行緒與進度的處理

簡介:

        開發過程中經常遇到一些費時的操作介面,比如統計某個磁碟分割槽的資料夾或者檔案數目,如果分割槽很大或者檔案過多的話,處理不好就會造成“假死”的情況,或者報“執行緒間操作無效”的異常,為了解決這個問題,可以使用委託來處理,在.net2.0中還可以用BackgroundWorker類。BackgroundWorker類是.net 2.0裡新增加的一個類,對於需要長時間操作而不需要使用者長時間等待的情況可以使用這個類。

注意:

        確保在 DoWork 事件處理程式中不操作任何使用者介面物件。而應該通過 ProgressChanged 和 RunWorkerCompleted 事件與使用者介面進行通訊。

案例:

        引用:System.dll       下載:http://download.csdn.net/detail/qq_33538554/9690615

原始碼:

----------------------MainWindow.xaml

<Windowxmlns:dxc="http://schemas.devexpress.com/winfx/2008/xaml/charts"  
        x:Class

="MobilePlugfest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MobilePlugfest"
        Height
="300" Width="400"
        Background="Transparent"
        WindowStyle="None"
        AllowsTransparency="True">
    <Window.Resources>
        <ResourceDictionary>
            <!--引用進度條-->
            <local:ValueToProcessConverter x:Key="ValueToProcessConverter"/>
            <!--設定字型顏色-->
            <Style  x:Key="lab" TargetType="Label">
                <Setter Property="Foreground"Value="Red"></Setter>
            </Style>
            <!--系統提示字型顏色-->
            <Style  x:Key="TextBox" TargetType="TextBox">
                <Setter Property="Foreground"Value="Red"></Setter>
            </Style>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <!--設定窗體透明化-->
        <Border CornerRadius="5"BorderThickness="2" BorderBrush="White"Opacity="0.8">
            <Border.Effect>
                <DropShadowEffect ShadowDepth="0"Color="#FF414141"BlurRadius="8"/>
            </Border.Effect>
            <Border Background="Black"Opacity="0.5" CornerRadius="5">
                <!--系統提示框-->
                <TextBox  Name="txtLog"Background="Black"Height="250"Width="200"Margin="0,0,140,0"Style="{StaticResourceResourceKey=TextBox}"/>
            </Border>
        </Border>
        <!--進度條-->
        <ProgressBar Minimum="0"Maximum="100"Name="progressBar"Height="60"Width="60"Margin="200,0,0,180">
            <ProgressBar.Template>
                <ControlTemplate TargetType="ProgressBar">
                    <Border Background="{TemplateBindingValue,Converter={StaticResourceValueToProcessConverter},ConverterParameter=200}"
                            MouseLeftButtonDown="Border_MouseLeftButtonDown_1"/>
                </ControlTemplate>
            </ProgressBar.Template>
        </ProgressBar>
        <!--顯示進度-->
        <Label Name="labTip" Style="{StaticResourcelab}"Height="100"Width="100"Margin="240,0,0,0"/>
    </Grid>
</Window>

----------------------MainWindow.xaml.cs

using SocketStd;//SocketStd
using System;
using System.Collections.Generic;
using System.ComponentModel;//BackgroundWorker
using System.Linq;
using System.Net;
using System.Net.Sockets;//TcpClient
using System.Text;
using System.Threading;//Thread
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Configuration;//ConfigurationManager

namespace MobilePlugfest
{
    /// <summary>
    /// MainWindow.xaml 的互動邏輯
    /// </summary>
    public partial class MainWindow : Window
    {
        BackgroundWorker bgWorker;
        double scannedCount = Convert.ToDouble(ConfigurationManager.AppSettings["ScannedCount"]);
        int runningThreadCount = Convert.ToInt32(ConfigurationManager.AppSettings["RunningThreadCount"]);
        static int maxThread = Convert.ToInt32(ConfigurationManager.AppSettings["MaxThread"]);
        string host = ConfigurationManager.AppSettings["Host"].ToString();
        int port = Convert.ToInt32(ConfigurationManager.AppSettings["Port"]);
        int startIP = Convert.ToInt32(ConfigurationManager.AppSettings["StartIP"]);
        int endIP = Convert.ToInt32(ConfigurationManager.AppSettings["EndIP"]);
        string addresIP = ConfigurationManager.AppSettings["AddresIP"].ToString();

        public MainWindow()
        {
            bgWorker = new BackgroundWorker();
            //進度更新報告true
            bgWorker.WorkerReportsProgress = true;
            //非同步取消true
            bgWorker.WorkerSupportsCancellation = true;
            //後臺操作任務
            bgWorker.DoWork += DoWork_Handler;
            //後臺操作進行時
            bgWorker.ProgressChanged += ProgressChanged_Handler;
            //後臺操作完成
            bgWorker.RunWorkerCompleted += RunWorkerCompleted_Handler;
        }
        /// <summary>
        /// 進度條的點選事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Border_MouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
        {
            if (!bgWorker.IsBusy)
            {
                if (txtLog.Text != "") txtLog.Text = "";
                scannedCount = 0;
                //開始執行後臺操作任務
                bgWorker.RunWorkerAsync();
            }
        }
        /// <summary>
        /// 後臺操作任務
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DoWork_Handler(object sender, DoWorkEventArgs e)
        {
            double total = Convert.ToDouble(endIP - startIP + 1);
            for (int ip = startIP; ip <= endIP; ip++)
            {
                if (bgWorker.CancellationPending)
                {
                    e.Cancel = true;
                    break;
                }
                //IP地址段,預設:192.168.1.
                host = addresIP + ip.ToString();
                //帶引數的多執行緒執行
                Thread thread = new Thread(() => Scan(host, port));
                thread.IsBackground = true;
                thread.Start();
                UpdateLabText(labTip, string.Format("正在掃描...\r\n第{0}臺\r\n共{1}臺\r\n進度:{2}%", scannedCount, total, Convert.ToInt32((scannedCount / total) * 100)));
                bgWorker.ReportProgress(Convert.ToInt32((scannedCount / total) * 100));
                runningThreadCount++;
                Thread.Sleep(10);//節省cpu資源
                //迴圈,直到某個執行緒工作完畢才啟動另一新執行緒,也可以叫做推拉窗技術
                while (runningThreadCount >= maxThread) ;
            }
            //空迴圈,直到所有埠掃描完畢
            do
            {
                UpdateLabText(labTip, string.Format("正在掃描...\r\n第{0}臺\r\n共{1}臺\r\n進度:{2}%", scannedCount, total, Convert.ToInt32((scannedCount / total) * 100)));
                bgWorker.ReportProgress(Convert.ToInt32((scannedCount / total) * 100));
                Thread.Sleep(10);
            } while (runningThreadCount > 0);
        }
        /// <summary>
        /// 後臺操作進行時
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ProgressChanged_Handler(object sender, ProgressChangedEventArgs e)
        {
            progressBar.Value = e.ProgressPercentage;
        }
        /// <summary>
        /// 後臺操作完成
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RunWorkerCompleted_Handler(object sender, RunWorkerCompletedEventArgs e)
        {
            labTip.Content = "掃描完成!";
        }
        /// <summary>
        /// 判斷ip埠是否為開放狀態
        /// </summary>
        /// <param name="m_host"></param>
        /// <param name="m_port"></param>
        public void Scan(string m_host, int m_port)
        {
            //高階的TcpClient類
            TcpClient tc = new TcpClient();
            //設定超時時間
            tc.SendTimeout = tc.ReceiveTimeout = 2000;
            try
            {
                //同步方法
                //IPAddress ip = IPAddress.Parse(host);
                //IPEndPoint IPendp = new IPEndPoint(ip, port);
                //tc.Connect(IPendp);

                //非同步方法
                IAsyncResult oAsyncResult = tc.BeginConnect(m_host, m_port, null, null);
                //1000為超時時間
                oAsyncResult.AsyncWaitHandle.WaitOne(1000, true);
                if (tc.Connected)
                {
                    //如果連線上,證明此埠為開放狀態
                    UpdateListBox(txtLog, m_host + ":" + m_port.ToString());
                }
            }
            catch (System.Net.Sockets.SocketException e)
            {
                //容錯處理
                MessageBox.Show("Port {0} is closed", host.ToString());
                Console.WriteLine(e.Message);
            }
            finally
            {
                tc.Close();
                tc = null;
                scannedCount++;
                runningThreadCount--;
            }
        }

        delegate void SetLabCallback(System.Windows.Controls.Label lb, string text);
        /// <summary>
        /// 非同步控制元件Label
        /// </summary>
        /// <param name="lb"></param>
        /// <param name="text"></param>
        public void UpdateLabText(System.Windows.Controls.Label lb, string text)
        {
            //InvokeRequired
            if (System.Threading.Thread.CurrentThread != lb.Dispatcher.Thread)
            {
                //控制元件只能由建立它的執行緒來訪問。其他執行緒想訪問必須呼叫該控制元件的Invoke方法。Invoke有兩個引數,一個是委託方法,一個是引數值
                SetLabCallback d = new SetLabCallback(UpdateLabText);
                this.Dispatcher.Invoke(d, new object[] { lb, text });
            }
            else
            {
                lb.Content = text.Trim();
            }
        }

        delegate void SetListCallback(System.Windows.Controls.TextBox lstBox, string text);
        /// <summary>
        /// 非同步控制元件TextBox
        /// </summary>
        /// <param name="lstBox"></param>
        /// <param name="text"></param>
        private void UpdateListBox(System.Windows.Controls.TextBox lstBox, string text)
        {
            if (System.Threading.Thread.CurrentThread != lstBox.Dispatcher.Thread)
            {
                SetListCallback d = new SetListCallback(UpdateListBox);
                this.Dispatcher.Invoke(d, new object[] { lstBox, text });
            }
            else
            {
                ShowMsg("系統提示:", "" + text.Trim() + "");
                txtLog.Text = SingleObject.GetSingle().LogString.ToString();
                txtLog.SelectionStart = txtLog.Text.Length;
                txtLog.SelectionLength = 0;
                txtLog.ScrollToEnd();
            }
        }
        /// <summary>
        /// 系統提示模板
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="msg"></param>
        private void ShowMsg(string sender, string msg)
        {
            string time = DateTime.Now.ToString("hh:mm:ss");
            SingleObject.GetSingle().LogString.AppendLine(time + "    " + sender);
            SingleObject.GetSingle().LogString.AppendLine(msg);
            SingleObject.GetSingle().LogString.AppendLine();
        }
    }
}

---------------------App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0"sku=".NETFramework,Version=v4.5" />
    </startup>
  <appSettings>
    <!--已掃描埠數目-->
    <add key ="ScannedCount" value="0"/>
    <!--正在執行的執行緒數目-->
    <add key ="RunningThreadCount" value ="0"/>
    <!--最大工作執行緒數-->
    <add key ="MaxThread" value ="100"/>
    <!--IP地址-->
    <add key ="Host" value ="null"/>
    <!---->
    <add key ="Port" value ="80"/>
    <!--Ip數量-->
    <add key ="EndIP" value ="255"/>
    <!--Ip 192.168.1.1-->
    <add key ="StartIP" value ="1"/>
    <!--IP地址段-->
    <add key ="AddresIP" value ="192.168.1."/>
  </appSettings>
</configuration>

----------------------SingleObject.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace SocketStd
{
    public class SingleObject
    {
        private static SingleObject _singleObject;
        private SingleObject()
        {
            this.LogString = new StringBuilder();
            this.ConnectedSocket = new Dictionary<string,Socket>();
            this.IsMove = false;
            this.LastSendedFile = "";
        }
        public static SingleObject GetSingle()
        {
            if (_singleObject == null)
            {
                _singleObject = new SingleObject();
            }
            return _singleObject;
        }
        private StringBuilder_logString;
        public StringBuilderLogString
        {
            get
            {
                return _logString;
            }
            set
            {
                _logString = value;
            }
        }
        private Dictionary<string,Socket> _connectedSocket;
        public Dictionary<string,Socket> ConnectedSocket
        {
            get
            {
                return _connectedSocket;
            }
            set
            {
                _connectedSocket = value;
            }
        }
        private bool _isMove;
        public bool IsMove
        {
            get
            {
                return _isMove;
            }
            set
            {
                _isMove = value;
            }
        }
        private string _lastSendedFile;
        public string LastSendedFile
        {
            get
            {
                return _lastSendedFile;
            }
            set
            {
                _lastSendedFile = value;
            }
        }
    }
}
---------------------ValueToProcessConverter.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;

namespace MobilePlugfest
{
    public class ValueToProcessConverter:IValueConverter
    {
        private const double Thickness = 8;
        private const double Padding = 1;
        private const double WarnValue = 60;
        private const int SuccessRateFontSize = 34;
        private static readonly SolidColorBrush NormalBrush;
        private static readonly SolidColorBrush WarnBrush;
        private static readonly Typeface SuccessRateTypeface;

        private string percentString;
        private PointcenterPoint;
        private double radius;

        static ValueToProcessConverter()
        {
            NormalBrush=new SolidColorBrush(Colors.Green);
            WarnBrush=new SolidColorBrush(Colors.Red);
            SuccessRateTypeface=new Typeface(newFontFamily("MSYH"),newFontStyle(),newFontWeight(),newFontStretch());
        }

        public ValueToProcessConverter()
        {

        }

        public object Convert(objectvalue,Type targetType, objectparameter, System.Globalization.CultureInfoculture)
        {
            if (value is double && !string.IsNullOrEmpty((string)parameter))
            {
                double arg = (double)value;
                double width = double.Parse((string)parameter);
                radius = width / 2;
                centerPoint = new Point(radius, radius);
                return DrawBrush(arg, 100, radius, radius, Thickness, Padding);
            }
            else
            {
                throw new ArgumentException();
            }
        }

        public object ConvertBack(objectvalue,Type targetType, objectparameter, System.Globalization.CultureInfoculture)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 根據角度獲取座標
        /// </summary>
        /// <param name="CenterPoint"></param>
        /// <param name="r"></param>
        /// <param name="angel"></param>
        /// <returns></returns>

        private PointGetPointByAngel(PointCenterPoint, double r, double angel)
        {
            Point p = new Point();
            p.X = Math.Sin(angel * Math.PI / 180) * r + CenterPoint.X;
            p.Y = CenterPoint.Y - Math.Cos(angel *Math.PI / 180) * r;
            return p;
        }

        /// <summary>
        /// 根據4個座標畫出扇形
        /// </summary>
        /// <param name="bigFirstPoint"></param>
        /// <param name="bigSecondPoint"></param>
        /// <param name="smallFirstPoint"></param>
        /// <param name="smallSecondPoint"></param>
        /// <param name="bigRadius"></param>
        /// <param name="smallRadius"></param>
        /// <param name="isLargeArc"></param>
        /// <returns></returns>

        private GeometryDrawingArcGeometry(PointbigFirstPoint,Point bigSecondPoint, Point smallFirstPoint, Point smallSecondPoint,doublebigRadius, double smallRadius, bool isLargeArc)
        {
            PathFigure pathFigure = new PathFigure { IsClosed = true };
            pathFigure.StartPoint = bigFirstPoint;
            pathFigure.Segments.Add(
              new ArcSegment
              {
                  Point = bigSecondPoint,
                  IsLargeArc = isLargeArc,
                  Size = new Size(bigRadius, bigRadius),
                  SweepDirection = SweepDirection.Clockwise
              });
            pathFigure.Segments.Add(new LineSegment { Point = smallSecondPoint });
            pathFigure.Segments.Add(
             new ArcSegment
             {
                 Point = smallFirstPoint,
                 IsLargeArc = isLargeArc,
                 Size = new Size(smallRadius, smallRadius),
                 SweepDirection = SweepDirection.Counterclockwise
             });
            PathGeometry pathGeometry = new PathGeometry();
            pathGeometry.Figures.Add(pathFigure);
            return pathGeometry;
        }

        /// <summary>
        /// 根據當前值和最大值獲取扇形
        /// </summary>
        /// <param name="value"></param>
        /// <param name="maxValue"></param>
        /// <returns></returns>

        private GeometryGetGeometry(doublevalue, double maxValue, double radiusX, double radiusY, double thickness, double padding)
        {
            bool isLargeArc = false;
            double percent = value / maxValue;
            percentString = string.Format("{0}%",Math.Round(percent * 100));
            double angel = percent * 360D;
            if (angel > 180) isLargeArc = true;
            double bigR = radiusX;
            double smallR = radiusX - thickness + padding;
            Point firstpoint = GetPointByAngel(centerPoint, bigR, 0);
            Point secondpoint = GetPointByAngel(centerPoint, bigR, angel);
            Point thirdpoint = GetPointByAngel(centerPoint, smallR, 0);
            Point fourpoint = GetPointByAngel(centerPoint, smallR, angel);
            return DrawingArcGeometry(firstpoint, secondpoint, thirdpoint, fourpoint, bigR, smallR, isLargeArc);
        }

        private void DrawingGeometry(DrawingContextdrawingContext,double value, doublemaxValue, double radiusX, double radiusY, double thickness, double padding)
        {
            if (value != maxValue)
            {
                SolidColorBrush brush;
                if (value < WarnValue)
                {
                    brush = WarnBrush;
                }
                else
                {
                    brush = NormalBrush;
                }
                drawingContext.DrawEllipse(null,newPen(newSolidColorBrush(Color.FromRgb(0xdd, 0xdf, 0xe1)), thickness), centerPoint, radiusX, radiusY);
                drawingContext.DrawGeometry(brush, newPen(), GetGeometry(value, maxValue, radiusX, radiusY, thickness, padding));
                FormattedText formatWords = new FormattedText(percentString,
                    CultureInfo.CurrentCulture,
                    FlowDirection.LeftToRight,
                    SuccessRateTypeface,
                    SuccessRateFontSize,
                    brush);
                Point startPoint = new Point(centerPoint.X - formatWords.Width / 2, centerPoint.Y - formatWords.Height / 2);
                drawingContext.DrawText(formatWords, startPoint);
            }
            else
            {
                drawingContext.DrawEllipse(null,newPen(NormalBrush, thickness), centerPoint, radiusX, radiusY);
                FormattedText formatWords = new FormattedText("100%",
                    CultureInfo.CurrentCulture,
                    FlowDirection.LeftToRight,
                    SuccessRateTypeface,
                    SuccessRateFontSize,
                    NormalBrush);
                Point startPoint = new Point(centerPoint.X - formatWords.Width / 2, centerPoint.Y - formatWords.Height / 2);
                drawingContext.DrawText(formatWords, startPoint);
            }
            drawingContext.Close();
        }

        /// <summary>
        /// 根據當前值和最大值畫出進度條
        /// </summary>
        /// <param name="value"></param>
        /// <param name="maxValue"></param>
        /// <returns></returns>

        private VisualDrawShape(doublevalue, double maxValue, double radiusX, double radiusY, double thickness, double padding)
        {
            DrawingVisual drawingWordsVisual =newDrawingVisual();
            DrawingContext drawingContext = drawingWordsVisual.RenderOpen();
            DrawingGeometry(drawingContext, value, maxValue, radiusX, radiusY, thickness, padding);
            return drawingWordsVisual;
        }

        /// <summary>
        ///根據當前值和最大值畫出進度條
        /// </summary>
        /// <param name="value"></param>
        /// <param name="maxValue"></param>
        /// <returns></returns>

        private BrushDrawBrush(doublevalue, double maxValue, double radiusX, double radiusY, double thickness, double padding)
        {
            DrawingGroup drawingGroup = new DrawingGroup();
            DrawingContext drawingContext = drawingGroup.Open();
            DrawingGeometry(drawingContext, value, maxValue, radiusX, radiusY, thickness, padding);
            DrawingBrush brush = new DrawingBrush(drawingGroup);
            return brush;
        }
    }
}

截圖: