Issue
I’m coding the backend portion of a software and at some point I need my user to choose some things, it can be a convoluted process, the user can even cancel the selection at any point.
From the back end I’d like to do something like:
private async void StartAction()
{
//some code
var SelectedItem = await UI.RequestUserToChooseItem();
// some final code using the selected item
}
Here I don’t know how to handle cancellation, but I can send null and assume that if the SelectedItem
is null it was canceled.
But what about the UI portion of it? How do I handle it to return the call when the thing is selected by the user?
I need to perform some steps here: (this is pseudocode, I don’t even know where to start)
public List<Item> RequestUserToChooseItem()
{
PrepareItemsInList();
ShowSelectionPanel();
List<Items> SelectedItemsFromPanel = WaitForUserToChose(); //???????
return SelectedItemsFromPanel;
}
And then we have the cancel button:
private void CancelButtonClicked(object sender, EventArgs e)
{
CancelWaitedSelectionProcessAndReturnNull(); //????
}
Solution
You can use a TaskCompletionSource to signal the choices. Something like
private TaskCompletionSource<MyOptions> tcs;
public Task<MyOptions> ShowPanelAndWaitForSelection(){
// show panel and do other initialization
tcs = new TaskCompletionSource<MyOptions>();
return tcs.Task;
}
public void OnOptionSelection(MyOptions value) => tcs.SetResult(value);
public void OnCanceled() => tcs.SetCanceled();
When if the task is canceled, any awaiter will get a OperationCanceledException
, so your code would normally look something like:
try{
...
var selectedOption = await ShowPanelAndWaitForSelection();
...
}
catch(OperationCanceledException){
// Handle cancellation
}
catch(Exception e){
// Handle actual errors
}
This assumes your UI is non-modal, like showing and hiding panels in the same form. If you use modal dialogs for each step you do not need any async code.
This style essentially uses the compiler to generate a state machine, instead of writing such a state machine by hand. I think this can be a useful style to handle specific cases, since you can make a decision tree using regular constructs like if/while etc. But it may not always be a net positive, and it may trip up developers that do not expect it.
Answered By – JonasH
Answer Checked By – Willingham (BugsFixing Volunteer)