0

I'm working on a WPF application with telerik controls.

I'm using RadListBox which is bind to the collection. When i select each RadListBoxItem a detailed view of the BindedItem will be shown in the nearby panel. Only one RadListBoxItem can be selected at a time.

In the below logic i'm making the following functionality,

  1. Edit
  2. Switch to new
  3. Alert for Save or Discard
  4. Save/Discard
  5. Load the selected

Now the issue is When the alert is thrown for Save/Discard, if i click save then the save process is initiated but before the save completed the load is also running in another thread. I have to check if the save is complete and start the load process.

//XAML Code:

<telerik:RadListBox x:Name="lstMarketSeries" ItemsSource="{Binding SCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True, ValidatesOnDataErrors=True}" SelectedItem="{Binding SelectedMSeries, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" telerik:StyleManager.Theme="Windows8"> 
<i:Interaction.Triggers>
   <i:EventTrigger EventName="SelectionChanged">
     <i:InvokeCommandAction Command="{Binding LoadSelected}"/>
   </i:EventTrigger>
</i:Interaction.Triggers>
</telerik:RadListBox> 

//ViewModel:

public ICommand LoadSelected { get { return new RelayCommand(LoadSelectedSDetails); } }

/// <summary>
/// Load selected series
/// </summary>
private async void LoadSelectedSDetails()
{
   if (SCollection != null)
   {
       if (!IsChanged())
       {
           bw = new BackgroundWorker();
           bw.RunWorkerAsync();

           bw.DoWork += (s, e) =>
           {
                //Loading functionality here( Have to check if the save is complete)
           };

           bw.RunWorkerCompleted += (s, e) =>
           {
                IsBusy = false;
           };
       }
       else
       { 
           await PutTaskDelay();  // This delay is to wait for save to complete
           LoadSelectedSDetails();
       }

/// <summary>
/// Check if collection is changed
/// </summary>
private bool IsChanged()
{
    bool IsChanged = false;
    if (SCollection != null)
       IsChanged = SCollection.Where(x => x.IsChanged || x.IsNew).Count() > 0;

    if (IsChanged)
    {
       if (ShowMessages.SaveOrDiscardBox())
       {
          SaveAllDetails(); // Saving runs in a separate thread.
       }
       else
       {
          //Discard functionality goes here
       }
    }

    return IsChanged;
}

Kindly help on this issue.

A Coder
  • 3,039
  • 7
  • 58
  • 129
  • I guess you're confusing `ListBoxItem` with the list box itself. If you have 10 items in collection, you're not creating 10 listboxes, but 10 list box items in a single listbox. – Sriram Sakthivel Mar 09 '16 at 07:03
  • @SriramSakthivel : Yes, thanks. Have updated the question. – A Coder Mar 09 '16 at 07:04
  • Just remove the `async/await` that make the execution synchronous. If you don't want to block the UI , run both the Action with the same `WorkerThread` one after another and use `Dispatcher` for UI updates. Another option would be [Backgroung Worker](http://stackoverflow.com/a/5483644/2819451) – Gopichandar Mar 09 '16 at 07:29
  • @Gopichandar : I cant make both the method in a single thread since i need to call those method separate at times. If possible can u post an answer with code sample using same `BGWorker` and `Dispatcher`? – A Coder Mar 09 '16 at 08:42
  • 1
    You're breaking the Single Responsibility Principle with `IsChanged`. It's both returning that the item has changed _and_ saving the item if `isChanged` is true. – moswald Mar 09 '16 at 14:24
  • @moswald : If it is not changed then it returns false. Correct? – A Coder Mar 10 '16 at 04:31
  • You should _just_ return `true` or `false` and not cause a side-effect (like saving). It will simplify your code a bit, and might show you a clear way to do a save-then-load. – moswald Mar 10 '16 at 12:37

1 Answers1

0

If you want to do something after a Task is executed you can use ContinueWith:

       var saveTask = new Task<ReturnedObject>(() =>  //if you don't need values in order to update the UI you can use Task not Task<T>
                    {
                      //do your save action and return something if you want 
                    });

                    //start thread
                    saveTask.Start();

                    saveTask.ContinueWith(previousTask =>
                    {//after the first thread is completed, this point will be hit

                        //loading action here

                        //UI region if needed
                            Application.Current.Dispatcher.BeginInvoke((ThreadStart)delegate
                            {
                              //update UI
                            }, DispatcherPriority.Render);               

                    }, TaskScheduler

    .FromCurrentSynchronizationContext());
A Coder
  • 3,039
  • 7
  • 58
  • 129
Dragos Stoica
  • 1,753
  • 1
  • 21
  • 42