0

I’m new to asynchronous processing in .net (honestly to .net at all) and need help with the following problem.

To bring it to the point, I need to know how to handle multiple calls of a synchronized method.

I got the following C# MVVM application:

application

Both Datagrids are binded to a Viewmodel. The First Datagrid is the input for the second. Every time a checkbox in the first Datagrid is clicked, a DelegateCommand is triggered, to refresh the content of the second datagrid.

The refreshment works like in this example:

<DataGrid AutoGenerateColumns="False"
    ItemsSource="{Binding Path=Datagrid1}" >
    <DataGrid.Columns>
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <CheckBox IsChecked="{Binding Boolean, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                    Command="{Binding DataContext.CheckBoxDataGrid1Click, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>
ObservableCollection<string> col1 = ObservableCollection<string>();
ObservableCollection<string> col2 = ObservableCollection<string>();

DelegateCommand CheckBoxDataGrid1Click = new DelegateCommand(CheckBoxDataGrid1ClickExecute);

// Needs to be async, so that the UI thread is will not be freezed.
private async void CheckBoxDataGrid1ClickExecute()
{
    var ui = TaskScheduler.FromCurrentSynchronizationContext();

    await Task.Factory.StartNew(
    () =>
    {
        return getNewObjects();

    }).ContinueWith(t =>
    {
        col2 = t.Result;
    }, ui);
}

[MethodImpl(MethodImplOptions.Synchronized)]
public ObservableCollection<string> getNewObjects()
{
    // Here is a long processing SQL Query
    // So lets wait a few seconds
    Thread.Sleep(5000);

    // return dummy in this example
    return new ObservableCollection<string>();
}

I got problems, if someone click a checkbox (in Datagrid1), when the processing of the last click has not finished.

My first idea was to make the getNewObjects() method synchronized. This solved a few problems. But, multiple clicks cause now a queue of refreshments. On the one hand I don’t know the sequence of the calls. On the other hand I’m just interested in the result of the last click.

The second idea was to modify the getNewObjects() method, so that it can be cancelled by CancellationToken. (I already started a question here - C# Synchronized Method call kills current processing). This way does not work too. The method contains a SQL query which cannot be cancelled easily in this case. I’m not able to modify the execution of the query or the SqlCommand.

To sum up: Is there a way to discard the current processing of the refreshment and start the new one? If not, how can I handle the Queue of calls? I need to make sure, that the last call will be the content of the second Datagrid, even if there is another one finishing later.

I'm open to any advise to solve tis problem. Maybe there is a better way of refreshing this second Datagrid, everytime Datagrid 1 has changed.

Community
  • 1
  • 1
theawak3r
  • 7
  • 4
  • you could prevent multiple clicks by disabling the check box when it is clicked and enabling it after the asynchronous event has finished and the 2nd data grid is refreshed. – David Green May 09 '17 at 10:46
  • 1.Use cancellation token. 2. Just make a check before "col2 = t.Result" if the task was not cancelled (I understand that that you can't cancel your sql query, but for the system it will look like that you have cancelled the task). – Leonid Malyshev May 09 '17 at 11:00
  • @DavidGreen I got this idea too. But I thought this would be a bad look and feel of the application. If I want to click 4 items in the first Datagrid, I need to wait everytime I clicked, even if I'm just interested in the last result. – theawak3r May 09 '17 at 11:00
  • @LeonidMalyshev Thank you very much. So easy. I spend now 5 hours to solve this. I add to the Task.Factory.StartNew a CancellationToken and save the previous token in the Viewmodel. In every call i cancel this previous token, if it exists. In the result i check like if(token.IsCancellationRequested) return; Is this a safe procedure? It should be impossible, that another result besides the last click is returned. – theawak3r May 09 '17 at 11:23
  • I usually (if ctx is CancellationTokenSource) use next steps 1. ctx?.Cancel() //to check for null also 2. ctx = new CancellationTokenSource(); – Leonid Malyshev May 09 '17 at 13:53

0 Answers0