Indeterminate Progress Bar

£可爱£侵袭症+ 提交于 2019-11-29 08:56:52

Hi I was a bit fast here, the methods you were using did not have any async-await overloads. So you could use the old BackgroundWorker. I've provided a really simple example for you here, made quickly (making food). The (unrun) example will only report progress 0 or 100, but it will not freeze your UI atleast. When reporting progress you send an int (the progress) and a userstate object, which may be whatever you want to send. Just cast it and do what you want :)

public class TestViewModel : INotifyPropertyChanged
{
    private int progress;
    private BackgroundWorker bgWorker;
    private bool isBusy;
    private readonly Dispatcher dispatcher;
    private ObservableCollection<DriveInfo> cdRoms;

    public Int32 Progress
    {
        get { return progress; }
        set
        {
            if (value == progress) return;
            progress = value;
            OnPropertyChanged();
        }
    }

    public bool IsBusy
    {
        get { return isBusy; }
        set
        {
            if (value.Equals(isBusy)) return;
            isBusy = value;
            OnPropertyChanged();
        }
    }

    public ICommand ImportCDFilePathCommand
    {
        get
        {
            return new RelayCommand(ImportReagentLotFilePath);
        }
    }


    public ObservableCollection<DriveInfo> CdRoms
    {
        get { return cdRoms; }
        set
        {
            if (Equals(value, cdRoms)) return;
            cdRoms = value;
            OnPropertyChanged();
        }
    }

    // This one made your app crash if you defined it directly in the xaml as datacontext and not were using a viewmodellocator
    public TestViewModel(Dispatcher dispatcher) // ugh I'm sure there is an interface for this, feed your UI dispatcher here
    {
        this.dispatcher = dispatcher;
    }

    // Add this one!
    public TestViewModel()
    {
        this.dispatcher = App.Current.Dispatcher; // Bad pie
    }


    private void ImportReagentLotFilePath()
    {

        IsBusy = true;
        Progress = 0;
        bgWorker = new BackgroundWorker { WorkerReportsProgress = true, WorkerSupportsCancellation = true };
        bgWorker.DoWork += bgWorker_DoWork;
        bgWorker.ProgressChanged += bgWorker_ProgressChanged;
        bgWorker.RunWorkerCompleted += bgWorker_RunWorkerCompleted;
        bgWorker.RunWorkerAsync(/*whatever parameter you want goes here*/);
    }

    void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        // you are done! 
        Progress = 100;
        CdRoms =  new ObservableCollection<DriveInfo>(e.UserState as IEnumerable<DriveInfo>);
    }

    void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        // Notifty your gui changes here forinstance, this method will be called on the gui thread. Just cast/parse what you feed
        Progress = e.ProgressPercentage;
        if (Progress == 100)
            IsBusy = false;
    }

    void bgWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        try
        {
            DriveInfo[] allDrives = DriveInfo.GetDrives();                
            bool cdRomExists = allDrives.Any(x => x.DriveType == DriveType.CDRom);
            IEnumerable<DriveInfo> cdroms = allDrives.Where(x => x.DriveType == DriveType.CDRom && allDrives.Any(y => y.IsReady));

            // reports the progress on the ui thread.... 
            bgWorker.ReportProgress(Progress,cdroms);
        }
        catch (Exception ex)
        {
            // errror handling + cancel run
            dispatcher.BeginInvoke((Action) (() => { IsBusy = false; Progress = 0; }));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator] // remove if you are not using R#
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Using tasks:

    // Alternatively use a task.....

    public ICommand TaskTestCommand
    {
        get
        {
            return new RelayCommand(DoStuffAsync);
        }
    }


    public Task DoStuffAsync()
    {
        Task tcs = Task.Factory.StartNew(() =>
        {
            try
            {
                // No awaits...  please note that anything bound in the gui must be changed on the dispatcher
                DriveInfo[] allDrives = DriveInfo.GetDrives();
                bool cdRomExists = allDrives.Any(x => x.DriveType == DriveType.CDRom);
                IEnumerable<DriveInfo> cdroms = allDrives.Where(x => x.DriveType == DriveType.CDRom && allDrives.Any(y => y.IsReady));
            }
            catch (Exception ex)
            {
                // handle your errors here. Note that you must check the innerexception for the real fault
                System.Diagnostics.Trace.WriteLine(ex.ToString());
            }
        }).ContinueWith((e) => { // this code is run when the task is completed...
            if(e.Exception!=null)
            {
                // hande error.. / 
            }
            else
            {
                // complete.. do whatever here
            }
        });     
        return tcs;       
    }

Hope it helps you in the right direction! I'm actually a bit surprised that there were no async-await overloads of the methods you are using, because it would allow you to use the nice async-await "statemachine-auto treader".

Cheers,

Stian

You can wrap any method in async using the following...

await Task.Factory.StartNew<T>(()=>{ ... Do something here...});

T is a generic to return a type. In your relay command example:

... = new RelayCommand(async () =>{ await DoSomethingAsync()});

Then...

private async DoSomethingAsync()
{
    await Task.Factory.StartNew(()=> {...});
}

Where your ... is whatever you want.

Kala J

With the help of Julien and NetSCAPE on SO WPF Chat, I figured out a few things I was doing wrong:

-I had dialog messages inside my background thread that should not be there in the method that was executing background threads. Thus, that's why I kept getting the STA error.

-All I needed to do was use Task.Run or Task.Factory.StartNew instead of pairing that with await/async because it was waiting for a task to be executed before running.

Here's how my answer looks now with removed dialog messages:

private void DoSomethingAsync()
{
    ProgressBarVisibility = Visibility.Visible;
    Task.Factory.StartNew(() => { PerformCDDetection(); }).ContinueWith(t => { ProgressBarVisibility = Visibility.Collapsed; });
}

public ICommand ImportFilePathCommand
{
    get
    {
        return new RelayCommand(() => { DoSomethingAsync(); });
    }    
}

private void PerformCDDetection()
{
    //Gets all the drives 
    DriveInfo[] allDrives = DriveInfo.GetDrives();

    //checks if any CD-Rom exists in the drives
    var cdRomExists = allDrives.Any(x => x.DriveType == DriveType.CDRom);

    // Get all the cd roms
    var cdRoms = allDrives.Where(x => x.DriveType == DriveType.CDRom && allDrives.Any(y => y.IsReady));

    if (cdRomExists.Equals(true))
    {
        // Loop through the cd roms collection
        foreach(var cdRom in cdRoms)
        {
            Console.WriteLine("Drive {0}", cdRom.Name);
            Console.WriteLine("  File type: {0}", cdRom.DriveType);

            if (cdRom.IsReady == true)
            {
                if (cdRom.DriveType == DriveType.CDRom)
                {
                    DirectoryInfo di = new DirectoryInfo(cdRom.RootDirectory.Name);

                    var file = di.GetFiles("*.xml", SearchOption.AllDirectories).FirstOrDefault();

                    if (file == null)
                    {
                        Console.WriteLine("failed to find file"); 
                    }
                    else
                    {
                        foreach (FileInfo info in di.GetFiles("*.xml", SearchOption.AllDirectories))
                        {
                            Debug.Print(info.FullName);
                            break;      // only looking for the first one
                        }

                        break;                            
                    }
                }
                else if (cdRom.IsReady == false)
                {
                    Console.WriteLine("Cd-ROM is not ready");
                    break;
                }    
            }
        }
        else
        {
            Console.WriteLine("CD ROM is not detected");
        }    
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!