Reputation: 173
I have a function in my viewmodel in which an asynchronous method is called. To execute a loading animation in the UI I have a boolean isLoading variable as binding. Immediately before calling the asynchronous method this variable is set to true and as soon as the method is finished it is set to false again. But it seems that the variable is not changed correctly. Anybody got an idea?
LoadFilenames snippet
public async Task LoadFileNames(string engineIdentifier, int displayMode) {
try {
ImageList.Clear();
IsLoading = true;
await MergeFiles(engineIdentifier);
IsLoading = false;
...
From my ViewModel
private bool _isLoading;
public bool IsLoading {
get { return _isLoading; }
set {
_isLoading = value;
OnPropertyChanged();
}
}
public void OnPropertyChanged([CallerMemberName] string name = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
From my View
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Shell.BackgroundColor="#696969"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:converters="clr-namespace:EngineCheck.Converters"
x:Class="EngineCheck.Views.EngineView"
xmlns:vm="clr-namespace:EngineCheck.ViewModels">
<ContentPage.BindingContext>
<vm:EngineViewModel x:Name="viewmodel"/>
</ContentPage.BindingContext>
Calling the Loading animation
<ActivityIndicator IsRunning="{Binding IsLoading}" IsVisible="{Binding IsLoading}"/>
All other bindings in the xaml file work smoothly. So the implementation of the ViewModel seems to be correct
UPDATE
My MergeFiles method
private async Task MergeFiles(string engineIdentifier) {
try {
string folderPath = prefs.Get("images_save_location", "");
string searchString = engineIdentifier;
if(Directory.Exists(folderPath)) {
string[] files = Directory.GetFiles(folderPath);
foreach(string filePath in files) {
string fileName = Path.GetFileName(filePath);
if(fileName.EndsWith("bmp") &&
fileName.Contains(searchString)) {
Utilities.Utilities.MergeBmpAndSvg(folderPath,
fileName[..^4]);
}
}
} else {
//TODO
}
} catch(Exception ex) {
logger.Error(ex.Message);
}
}
MergeBmpAndSvg method
public static void MergeBmpAndSvg(string path, string filename) {
try {
Bitmap bmpImage = new Bitmap(path + "/" + filename + ".bmp");
SvgDocument svgDocument = SvgDocument.Open(path + "/" + filename +
".svg");
Bitmap svgBitmap = svgDocument.Draw();
Bitmap resultImage = new Bitmap(bmpImage.Width, bmpImage.Height);
using(Graphics graphics = Graphics.FromImage(resultImage)) {
graphics.DrawImage(bmpImage, 0, 0);
graphics.DrawImage(svgBitmap, new System.Drawing.Point(0, 0));
}
resultImage.Save(path + "/" + filename + ".png");
bmpImage.Dispose();
svgBitmap.Dispose();
resultImage.Dispose();
} catch(Exception ex) {
logger.Error(ex.Message);
}
}
Upvotes: 0
Views: 388
Reputation: 8856
You may be running into a deadlock by mixing asynchronous code and synchronous code.
The following code does not run asynchronously, there are only synchronous (blocking) calls and nothing is being await
ed:
private async Task MergeFiles(string engineIdentifier) {
try {
string folderPath = prefs.Get("images_save_location", "");
string searchString = engineIdentifier;
if(Directory.Exists(folderPath)) {
string[] files = Directory.GetFiles(folderPath);
foreach(string filePath in files) {
string fileName = Path.GetFileName(filePath);
if(fileName.EndsWith("bmp") &&
fileName.Contains(searchString)) {
Utilities.Utilities.MergeBmpAndSvg(folderPath,
fileName[..^4]);
}
}
} else {
//TODO
}
} catch(Exception ex) {
logger.Error(ex.Message);
}
}
Note: Just marking a method as async
and using the Task
return type, will not make your code run asynchronously!
In order to make the above code, which may be a longer running CPU-bound operation, run in an asynchronous fashion, you should change its signature to void
(without async
!):
private void MergeFiles(string engineIdentifier) {
try {
string folderPath = prefs.Get("images_save_location", "");
string searchString = engineIdentifier;
if(Directory.Exists(folderPath)) {
string[] files = Directory.GetFiles(folderPath);
foreach(string filePath in files) {
string fileName = Path.GetFileName(filePath);
if(fileName.EndsWith("bmp") &&
fileName.Contains(searchString)) {
Utilities.Utilities.MergeBmpAndSvg(folderPath,
fileName[..^4]);
}
}
} else {
//TODO
}
} catch(Exception ex) {
logger.Error(ex.Message);
}
}
and then move the call to that method to a thread pool thread using Task.Run()
:
await Task.Run(() =>
{
MergeFiles(engineIdentifier);
});
Upvotes: 1