If you try to run an update in one of your properties marked with ObservableProperty from the CommunityTookit.Mvvm package in an async context, you'll get the following exception:
System.Runtime.Interop.COMException
Example:
private void MyButton_ClikEvent(Sender s, Args args)
{
Task.Run(async() =>
{
await AsyncTask();
ViewModel.Property = x; // This will throw an exception.
});
}
ComminutyTookit Repo - Original Question.
When you make changes to UI elements, you have to do it on the UI (main) thread.
The click event will run on the UI thread. So, the following code will run without errors.
private async void MyButton_ClikEvent(Sender s, Args args)
{
// The method will run on the UI thread.
// Then might run on a different thread inside `AsyncTask()`.
await AsyncTask();
// This will run on the UI thread (again).
// So, it's OK to change this property, which seems to be bound to a UI control.
ViewModel.Property = x;
}
But the following code will throw an exception because it makes changes to the UI on a non-UI thread.
private void MyButton_ClikEvent(Sender s, Args args)
{
// The method will run on the UI thread.
Task.Run(async () =>
{
// This will run on another thread.
await AsyncTask();
// Thus, this will throw an exception.
ViewModel.Property = x;
});
}
If, for some reason, you need to make changes to the UI on a non-UI thread, you can use the DispatcherQueue.
Not recommended but the following should work.
private void MyButton_ClikEvent(Sender s, Args args)
{
// The method will run on the UI thread.
Task.Run(async () =>
{
// This will run on another thread.
await AsyncTask();
this.DispatcherQueue.TryEnqueue(() =>
{
// This will run on the UI thread.
ViewModel.Property = x;
});
});
}
Since you are using the CommunityToolkit.Mvvm NuGet package, you should use the RelayCommand attribute instead of a Click
event handler.
So, on your view model class:
public partial SomeViewModel : ObservableObject
{
[ObservableProperty]
private string _someText = string.Empty;
[RelayCommand]
private Task SomeMethod(string newText)
{
SomeText = newText;
}
}
then on XAML:
<TextBox x:Name="SomeTextBox" />
<Button
Command="{x:Bind ViewModel.SomeMethodCommand}"
CommandParameter="{x:Bind SomeTextBox.Text, Mode=OneWay}" />