ohgross ohgross - 10 days ago 5
C# Question

UWP C# Return UI object to synchronous extension method on main thread

I am trying to write an extension method for the

Page
class that will return a user control which is a dynamically created child of the root
Grid
. The issue I am having is working with the
Content
property of the
Page
class.

The aim is to return the control for the purpose of checking the control's properties, and will mostly be called from the main thread.

Here is what I'm trying to do:

public static MyUserControl GetControlByTag(this Page page, int tag) {
//throws "marshalled from a different thread" exception
Grid rootGrid = page.Content as Grid;

if(rootGrid != null) {
var controls = rootGrid.Children.OfType<MyUserControl>();

foreach(MyUserControl control in controls)
{
if(control.Tag == tag) return control;
}

return null;
}


I have tried making a helper method of return type
Task<Grid>
to get the object, but I can't find a way to synchronously return the asynchronous Task, and I can't seem to find a way to synchronously return a value from
page.Dispatcher.RunAsync()
.

This is generally what the XAML layout looks like:

<Page>
<Grid>
...
...
...
<MyUserControl/>
<MyUserControl/>
<MyUserControl/>
<Grid/>
</Page>

Answer

I found the solution through MSDN:

public static MyUserControl GetControlByTag(this Page page, int tag) {
    MyUserControl rtn = null; 

    page.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
        Grid rootGrid = page.Content as Grid;

        if(rootGrid != null) {
            var controls = rootGrid.Children.OfType<MyUserControl>();

            foreach(MyUserControl control in controls)
            {
                if(control.Tag == tag) rtn = control;
            }
        }
    }).AsTask().Wait(); //Runs the dispatcher synchronously

    return rtn;
}

Reference post: https://social.msdn.microsoft.com/Forums/windowsapps/en-US/e15d4852-a601-4bd6-88b4-42480fd71e70/win-rt-awaiting-the-dispatcherrunasync?forum=winappswithcsharp

EDIT: A note, though, if this is to be used throughout the application: you may have to do a check to see if the method already has thread access. I haven't found an elegant way to do this as of yet, but the basics of it look like this:

public static MyUserControl GetControlByTag(this Page page, int tag) {
    Grid rootGrid = page.Content as Grid;

    if(page.Dispatcher.HasThreadAccess) {
        //Code to run
    }
    else {
        page.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
            //Code to run
        }).AsTask().Wait();
    }
}