简体   繁体   中英

Background Worker Call Method or something

I have a method/procedure which works well, however it takes ages to do its thing so I want to move it into a background worker so people can still use the app.

Here is the code. (I cut down as much as I could)

public partial class NetworkInformation : UserControl, INotifyPropertyChanged
    {
        public NetworkInformation()
        {
            InitializeComponent();
            Discovery();
        }
        public void Discovery()
        {
            GetIcon Icon = new GetIcon();
            BitmapImage IconOfComputer = null;
            List<DiscoveredComputer> NetworkedComputers = new List<DiscoveredComputer>();
            DirectoryEntry Discover = new DirectoryEntry("WinNT://Workgroup");
            BitmapImage On = Icon.LoadIcon(@"/Images/Icons/ComputerOn.ico");
            BitmapImage Off = Icon.LoadIcon(@"/Images/Icons/ComputerOff.ico");
            foreach (DirectoryEntry Node in Discover.Children)
            {
                try
                {
                    if (Node.Properties.Count > 0)
                    {
                        IconOfComputer = On;
                    }
                }
                catch
                {
                    IconOfComputer = Off;
                }
                if (Node.Name != "Schema") { NetworkedComputers.Add(new DiscoveredComputer { Image = IconOfComputer, ComputerName = Node.Name, MyToolTip = "Node Type = " + Node.SchemaEntry.Name }); }
            }
            ListView_LocalComputers.ItemsSource = NetworkedComputers;
        }
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(string PropertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
        }
    }

    public class DiscoveredComputer : INotifyPropertyChanged
    {
        private string _ComputerName;
        public string ComputerName
        {
            get { return _ComputerName; }
            set
            {
                _ComputerName = value;
                this.NotifyPropertyChanged("ComputerName");
            }
        }
        private BitmapImage _Image;
        public BitmapImage Image { 
            get { return _Image; }
            set
            { 
                _Image = value;
                this.NotifyPropertyChanged("Image");
            }
        }
        private String _MyToolTip;
        public String MyToolTip
        {
            get { return _MyToolTip; }
            set
            {
                _MyToolTip = value;
                this.NotifyPropertyChanged("ToolTip");
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(string PropertyName)
        {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
        }
    }
    public class GetIcon
    {
        public BitmapImage IconStorage { get; set; }
        public BitmapImage LoadIcon(String IconPath)
        {
            BitmapImage GeneratedIcon = new BitmapImage();
            GeneratedIcon.BeginInit();
            GeneratedIcon.UriSource = new Uri("pack://application:,,," + IconPath, UriKind.RelativeOrAbsolute);
            GeneratedIcon.EndInit();
            IconStorage = GeneratedIcon;
            return GeneratedIcon;
        }
    }
}

This all works awesomely, somehow...

Here is the code I:developed for my background worker

 public partial class MyBackgroundWorker : UserControl
    {
        WorkerData BGW;
        public MyBackgroundWorker()
        {
            InitializeComponent();

            BGW = new WorkerData();

            #region Workers Events
            BGW.ThisWorker.DoWork += new DoWorkEventHandler(Workers_DoWork);
            BGW.ThisWorker.ProgressChanged += new ProgressChangedEventHandler(Workers_Progress);
            BGW.ThisWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Workers_Completed);
            BGW.ThisWorker.WorkerReportsProgress = true;
            BGW.ThisWorker.WorkerSupportsCancellation = true;
            #endregion


        }
        public void RibbonButton_EventClickStart(object sender, RoutedEventArgs e)
        {
            BGW.ThisWorker.RunWorkerAsync();
        }
        public void UserForm_Loaded(object sender, RoutedEventArgs e)
        {
        }
        public void RibbonButton_EventClick(object sender, RoutedEventArgs e)
        {
            BGW.ThisWorker.CancelAsync();
        }
        public void Workers_DoWork(object sender, DoWorkEventArgs e)
        {

        }
        public void Workers_Progress(object sender, ProgressChangedEventArgs e)
        {
            BGW.ThisWorkersProgress = e.ProgressPercentage;
        }

        public void Workers_Completed(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled) { BGW.ThisWorkersResult = "Cancelled By User"; }
            else if (e.Error != null) { BGW.ThisWorkersResult = "Error Encountered: " + e.Error.Message; }
            else
            {
                BGW.ThisWorkersResult = "Task Completed Successfully";
                BGW.WorkersReturnObject = e.Result;
            }
        }
    }
    public class WorkerData
    {
        public BackgroundWorker ThisWorker { get; set; }
        public int ThisWorkersProgress { get; set; }
        public string ThisWorkersResult { get; set; }
        public object WorkersReturnObject { get; set; }
        public object ThisWorkersJob { get; set; }

        public WorkerData()
        {
            ThisWorker = new BackgroundWorker();
        }
    }

So how do I get my background worker to run the Discovery method I have created?

您需要在DoWork事件处理程序中进行工作。

I don't know if you need a whole separate class for this. I prefer to create these as I need them, on the fly. I think you'll get yourself shoehorned, where you'll use your class in multiple places and then decide you want to do something else in Workers_Completed in certain cases, or do something different when an error occurs in certain cases, and that one class could end up being a tangled-up pain. That's just my opinion though.

Also, you have to be very careful about touching the UI thread from your BackgroundWorker. In the example below, I'm passing in your node count to the DoWork event, instead of having it possibly touch a UI component directly. I'm also passing the list to the RunWorkerCompleted event, so that you're back in the main thread when it tries to attach the list to your ListView.

var bw = new BackgroundWorker();

bw.DoWork += (s, e) =>
  {
    var nodePropertiesCount = (int)e.Argument;

    // the guts of `Discovery` go in here

    e.Result = NetworkedComputers;
  };

bw.RunWorkerCompleted += (s, e) =>
  {
    if (e.Error != null)
    {
        // Task Completed Successfully
        ListView_LocalComputers = (List<DiscoveredComputer>)e.Result;
    }
    else
    {
        // Error Encountered
    }
  };

bw.RunWorkerAsync(Node.Properties.Count);

SLaks answer is correct, but you apparently don't understand what that means. I'd suggest taking the guts of Discover() and putting them in the Workers_DoWork() method like this:

public void Workers_DoWork(object sender, DoWorkEventArgs e)
{
    var backgroundWorker = sender as BackgroundWorker;
    GetIcon Icon = new GetIcon();
    BitmapImage IconOfComputer = null;
    List<DiscoveredComputer> NetworkedComputers = new List<DiscoveredComputer>();
    DirectoryEntry Discover = new DirectoryEntry("WinNT://Workgroup");
    BitmapImage On = Icon.LoadIcon(@"/Images/Icons/ComputerOn.ico");
    BitmapImage Off = Icon.LoadIcon(@"/Images/Icons/ComputerOff.ico");

    while (!backgroundWorker.CancellationPending)
    {
        foreach (DirectoryEntry Node in Discover.Children)
        {
            try
            {
                if (Node.Properties.Count > 0)
                {
                    IconOfComputer = On;
                }
            }
            catch
            {
                IconOfComputer = Off;
            }
            if (Node.Name != "Schema") { NetworkedComputers.Add(new DiscoveredComputer { Image = IconOfComputer, ComputerName = Node.Name, MyToolTip = "Node Type = " + Node.SchemaEntry.Name }); }
        }
        break;
    }
    if(backgroundWorker.CancellationPending)
    {
        e.Cancel = true;
    }
    else
    {
        e.Result = NetworkedComputers;
    }
}

And then modifying your Workers_Completed() like this:

public void Workers_Completed(object sender, RunWorkerCompletedEventArgs e)
{
  if (e.Cancelled) { BGW.ThisWorkersResult = "Cancelled By User"; }
  else if (e.Error != null) { BGW.ThisWorkersResult = "Error Encountered: " + e.Error.Message; }
  else
  {
      BGW.ThisWorkersResult = "Task Completed Successfully";
      //BGW.WorkersReturnObject = e.Result;
        //background worker can't touch UI components
        ListView_LocalComputers.ItemsSource = e.Result as List<DiscoveredComputer>;
  }
}

I suggest these changes, or something similar, because the background worker can't modify/access UI components (like your ListView), so it has to pass back the value to use for the ListView view its Result property. I also included a simple way of detecting cancellation; I'll leave progress reporting up to you to implement.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM