I have a C# UWP desktop application class that I am having issues with when trying to run code in another method that needs to access the UI thread. I have read a few other questions/answers already and tried some solutions, but so far nothing has worked.
I have a method called from a UI event that is accessing the UI okay. Within this, I am calling an awaited method (I need to do this for recursion and to perform some async file processing, not shown here) that will update the UI after individual items are processed. The problem I am running into is mainly a marshalling exception (System.Exception: The application called an interface that was marshalled for a different thread.), although some errors have cropped up from other things I have tried.
Initially, this was not an awaited async method, but I realized it needed to be based on how it behaved in the UI. Before I changed anything, the method was able to access the UI, even without any changes. So, I changed from "IUInit_ProcessDroppedItem(item);" to "await Task.Run(() => IUInit_ProcessDroppedItem(item));" -- Task.Run was needed because I could not compile otherwise since I could not await a void method, according to this). At this point, I started running into the marshalling exception.
Here is the current code for the initial UI event (a drag and drop operation) -
private async void IUInit_DragDropGrid_Drop(object sender, DragEventArgs e)
{
InitGlobalStatusMessage.Text = "Some text";
InitMessagesPanel.Children.Clear();
if (e.DataView.Contains(StandardDataFormats.StorageItems))
{
var items = await e.DataView.GetStorageItemsAsync();
foreach (IStorageItem item in items)
{
try
{
await Task.Run(() => IUInit_ProcessDroppedItem(item));
} catch { }
}
}
}
And here was my original code from the method that is called (IUInit_ProcessDroppedItem(item)). The marshalling exception occurs after creating the new TextBlock():
private async void IUInit_ProcessDroppedItem(IStorageItem item)
{
try
{
// For more context, here is some of the code running in the function, not directly relevant.
if (item.IsOfType(StorageItemTypes.Folder))
{
TextBlock msg = new TextBlock();
msg.Foreground = new SolidColorBrush(Colors.LightYellow);
msg.Text = "Folder processing : " + item.Name;
InitMessagesPanel.Children.Add(msg);
// Get a count of items in this folder (only recurse if there are items)
StorageFolder folder = (StorageFolder)item;
IReadOnlyList<IStorageItem> itemsList = await folder.GetItemsAsync();
if (itemsList.Count==0)
{
TextBlock msg2 = new TextBlock();
msg2.Foreground = new SolidColorBrush(Colors.LightYellow);
msg2.Text = " Nothing found in this folder (skipped)" + item.Name;
InitMessagesPanel.Children.Add(msg);
} else
{
for (int j = 0; j <= itemsList.Count; j++)
{
try
{
IUInit_ProcessDroppedItem(itemsList[j]);
}
catch (Exception ex) { }
}
}
}
}
catch (Exception ex)
{
TextBlock msg = new TextBlock();
msg.Foreground = new SolidColorBrush(Colors.OrangeRed);
msg.Text = "Processing failed" + item.Name;
InitMessagesPanel.Children.Add(msg);
}
}
There are a couple objects and variables from the XAML code and other code in the page code-behind, but it wasnt relevant to this so I edited it out.
My first attempted fix was to use the Window.Dispatcher fix suggested here. This didn't compile (An object reference is required for the non-static field, method, or property 'Window.Dispatcher')
var ignored = Window.Dispatcher.DispRunAsync(CoreDispatcherPriority.Normal, () =>
{
TextBlock msg = new TextBlock();
msg.Foreground = new SolidColorBrush(Colors.OrangeRed);
msg.Text = "Processing failed" + item.Name;
InitMessagesPanel.Children.Add(msg);
});
I then tweaked to use Window.Current, but this produced a NullReferenceException. From what I read, UWP desktop applications always have this as null.
var ignored = Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
TextBlock msg = new TextBlock();
msg.Foreground = new SolidColorBrush(Colors.OrangeRed);
msg.Text = "Processing failed" + item.Name;
InitMessagesPanel.Children.Add(msg);
});
Since this I have not been able to get this to work - does anyone know a way to properly access the UI from another method, or a simpler way to do this? It is not an option for me to do everything from the original UI method since the changes need to take place with each recursion. It is unclear to me if the more recent issues have actually fixed the marshalling issue and these are new issues, or the marshalling issue would still be present after fixing the new issues, but either way I am not sure exactly how to correct this.