UWP - I can't edit a field on the Application class from the UI thread

93 views Asked by At

I have a property Files of type ObservableCollection<Element> in my Application class.

public sealed partial class App : Application
{
    public string CurrentPath;

    public ObservableCollection<Element> Files;

    public async void RefreshFilesAsync()
    {
        Files.Clear();

        var folder = await StorageFolder.GetFolderFromPathAsync(CurrentPath);
        var foldersRequest = folder.GetFoldersAsync();
        var filesRequest = folder.GetFilesAsync();

        var folders = await foldersRequest;
        var files = await filesRequest;

        foreach (StorageFolder directory in folders)
        {
            Files.Add(new FolderElement(directory));
        }

        foreach (StorageFile file in files)
        {
            Files.Add(new FileElement(file));
        }
    }

    // ...

}

Element is a wrapper class of mine for StorageFile objects. This class is made so that setting a new value to the Name property calls the API to rename the actual file.

public class Element
{
    public string Name
    {
        get => StorageFile.Name;
        set =>
        {
            StorageFolder.RenameAsync(value, NameCollisionOption.GenerateUniqueName).Completed = (IAsyncAction act, AsyncStatus status) =>
            {
                (App.Current as App).RefreshFilesAsync();
            };
        }
    }

    public StorageFile StorageFile { get; }

    //...

}

I have a DataGrid linked to the Application property Files, so you can deduce that whenever I rename the column for the file name, the API is called to perform the real renaming.

Here's when my problem occur. The API call succeeds and the file is renamed, but inside RefreshFilesAsync(), when Files.Clear(); is called, I get an exception The application called an interface that was marshalled for a different thread.

What am I getting wrong?

1

There are 1 answers

0
Nerpson On BEST ANSWER

To run code with the UI thread, I used Window.Current.Dispatcher.RunAsync(), and put it in a separate async method. The exception does not appear anymore.

public string Name
{
    get => StorageFolder.Name;
    set => RenameAsync(value);
}

private async void RenameAsync(string newName)
{
    await StorageFolder.RenameAsync(newName, NameCollisionOption.GenerateUniqueName);
    await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => (App.Current as App).RefreshFilesAsync());
}