none
[UWP][C#] How To Delay Running A Method For A Specific Amount Of Time RRS feed

  • Question

  • Hello...

    I have an app that builds a text string, based on the contents of an ObservableCollection.
    The issue has been that the text string gets built before the ObservableCollection is filled.
    Is there a way to delay that string building?
    The preference would be for the ObservableCollection to trigger the string, but I'm not sure that's possible (how would it know when it's complete?).

    Thanks...
    JJ

    Friday, October 16, 2015 3:34 PM

Answers

  • But if you're populated the collection you must know the size which the collection changed can read. Regardless of the length CollectionChanged would seem to be the appropriate time to process if you want to run over the results. Although you probably want to put a delay or cancellation token if you expect a large number of changes.

    http://pauliom.wordpress.com

    Ok, that did populate the string before pressing the play button.
    Won't it rebuild the string every time an item is added to the ObservableCollection, though?
    If that's the case, I would think there would a performance hit.

    My code...

            private void CreateSsml(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                //throw new NotImplementedException();
    
                // empty the string
                texttoplay.Clear();
    
                // create text for the audio player
                // ssml allows things like language, voice, and speed
                texttoplay.Append("<?xml version='1.0' encoding='utf-8'?>");
                texttoplay.Append("<speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xml:lang='en-us'>");
                texttoplay.Append("<prosody rate='0.85'>");
                // fill in the content from the ObservableCollection
                foreach (Data.TodaysChapterCollection verse in Data.todayschaptercollection)
                {
                    // <s> defines a sentence
                    texttoplay.Append("<s>" + verse.Text + "</s>");
                }
                texttoplay.Append("</prosody>");
                texttoplay.Append("</speak>");
            }
    

    If it will rebuild the string, with each addition, I wonder if I could just add the "foreach()" bit with CollectionChanged.
    JJ

    Saturday, October 17, 2015 5:29 PM

All replies

  • You can use await  Task.Run(); to execute functions asynchronously.

    Here's an example:

            private async void Method()
            {
                ObservableCollection<string> collection; //Note that i used string as the model for the Observable collection. Replace it with yours.
    
                await Task.Run(new Action(() =>
                {
                    collection = new ObservableCollection<string>(); //Populate ObservableCollection here
    
                }));
    
                await Task.Run(new Action(() =>
                {
                    GenerateString();
                }));
            }
    
            private void GenerateString()
            {
                //Perform String Generation Here!
            }

    This will make sure all ObservableCollection population is completed before generating the string.

    Don't forget to mark as answer if it helps you out.


    Friday, October 16, 2015 3:51 PM
  • Hmmm...
    My ObservableCollection is created and filled in a portable class library.
    My StringBuilder is created and filled in the main project.

    Could the string be built, in the portable class library, and utilized, by the main project?
    I reference the PCL method with ProjectName.MethodName().
    Can I use ProjectName.StringName?
    JJ

    Friday, October 16, 2015 4:26 PM
  • Okay, yes you can. Just make the MethodName a Task and make it return an ObservableCollection<MyObject>.

    Example: In your PCL you can have something this:

            public static async Task<ObservableCollection<MyObject>> MethodName()
            {
                ObservableCollection<MyObject> collection = new ObservableCollection<MyObject>();
    
                await Task.Run(new Action(() =>
                {
                    collection = new ObservableCollection<MyObject>(); //Populate ObservableCollection here
                }));
    
                return collection;
            }

    In your Main project:

                var _collection = await ProjectName.MethodName(); //This gets the collection asynchroneously
    
                await Task.Run(new Action(() =>
                {
                    GenerateString(_collection);
                }));

    And the GenerateString method can be something like:

            private void GenerateString(ObservableCollection<MyObject> _collection)
            {
                //Generate StringBuilder Here and do some processing
                //Maybe something Like
    
                StringBuilder sb = new StringBuilder();
    
                foreach (var item in _collection)
                {
                    sb.Append(item.ToString());
                }
            }

    Awaiting your reply....

    Friday, October 16, 2015 5:01 PM
  • I assume you know the length of the collection so you could hook up collection changed and only write the string when collection changed == expected length

    http://pauliom.wordpress.com

    Friday, October 16, 2015 6:17 PM
  • I assume you know the length of the collection so you could hook up collection changed and only write the string when collection changed == expected length

    http://pauliom.wordpress.com


    I don't have an expected length.
    The collection's size changes, daily.
    JJ
    Friday, October 16, 2015 6:19 PM
  • But if you're populated the collection you must know the size which the collection changed can read. Regardless of the length CollectionChanged would seem to be the appropriate time to process if you want to run over the results. Although you probably want to put a delay or cancellation token if you expect a large number of changes.

    http://pauliom.wordpress.com

    Friday, October 16, 2015 9:04 PM
  • But if you're populated the collection you must know the size which the collection changed can read. Regardless of the length CollectionChanged would seem to be the appropriate time to process if you want to run over the results. Although you probably want to put a delay or cancellation token if you expect a large number of changes.

    http://pauliom.wordpress.com

    I'll look into CollectionChanged.
    Thanks...
    JJ
    Saturday, October 17, 2015 3:01 PM
  • Back to the original question...
    Is there a way to just put a delay on the method (like 30 seconds, or something)?
    Alternatively, maybe I can run the method after the page actually displays?
    The problem is that it's being run while the splash screen is still shown.
    JJ
    Saturday, October 17, 2015 3:03 PM
  • But if you're populated the collection you must know the size which the collection changed can read. Regardless of the length CollectionChanged would seem to be the appropriate time to process if you want to run over the results. Although you probably want to put a delay or cancellation token if you expect a large number of changes.

    http://pauliom.wordpress.com

    I'm trying this bit of code...

    Data.todayschaptercollection.CollectionChanged += CreateSsml();

    But it's giving an error of "Cannot implicitly convert type 'void' to 'System.Collections.Specialized.NotifyCollectionChangedEventHandler'".
    Is that because my CreateSsml() method is void (doesn't return anything)?
    I have this chunk of code in my public MainPage() method.

    Unfortunately, there are no examples on the CollectionChanged page.
    I'm just kind of guessing.
    JJ

    Saturday, October 17, 2015 3:43 PM
  • You need to create a func with the correct signature. Delete everything up to 'Changed' and allow visual studio to create the default function to support the event.

    http://pauliom.wordpress.com

    • Proposed as answer by IssueKiller Wednesday, October 28, 2015 9:58 AM
    Saturday, October 17, 2015 4:53 PM
  • Delete everything up to 'Changed' and allow visual studio to create the default function to support the event.

    That made me think that I should delete "Data.todayschaptercollection.Collection" and something would happen (which it didn't).
    I deleted everything AFTER "Changed" and IntelliSense told me that it needed "+=" or "-=", so I added that.
    Then it said "Press TAB to insert", so I did that.
    Now, I have the function that you were referring to.
    Maybe those steps will help someone else.
    JJ

    Saturday, October 17, 2015 5:22 PM
  • But if you're populated the collection you must know the size which the collection changed can read. Regardless of the length CollectionChanged would seem to be the appropriate time to process if you want to run over the results. Although you probably want to put a delay or cancellation token if you expect a large number of changes.

    http://pauliom.wordpress.com

    Ok, that did populate the string before pressing the play button.
    Won't it rebuild the string every time an item is added to the ObservableCollection, though?
    If that's the case, I would think there would a performance hit.

    My code...

            private void CreateSsml(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                //throw new NotImplementedException();
    
                // empty the string
                texttoplay.Clear();
    
                // create text for the audio player
                // ssml allows things like language, voice, and speed
                texttoplay.Append("<?xml version='1.0' encoding='utf-8'?>");
                texttoplay.Append("<speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xml:lang='en-us'>");
                texttoplay.Append("<prosody rate='0.85'>");
                // fill in the content from the ObservableCollection
                foreach (Data.TodaysChapterCollection verse in Data.todayschaptercollection)
                {
                    // <s> defines a sentence
                    texttoplay.Append("<s>" + verse.Text + "</s>");
                }
                texttoplay.Append("</prosody>");
                texttoplay.Append("</speak>");
            }
    

    If it will rebuild the string, with each addition, I wonder if I could just add the "foreach()" bit with CollectionChanged.
    JJ

    Saturday, October 17, 2015 5:29 PM
  • Yes, that's what I was referring to when I said you'll probably want a delay or cancellation token.

    http://pauliom.wordpress.com

    Saturday, October 17, 2015 10:01 PM