Reputation: 277
I'm working with the Philips Hue, and I need to get some information from the hue bridge before I can populate my application. The requests are made via HTTP/JSON. I have no issue when I run all my code async
, but when I try to break out my code so that I have a separate method to update the UI upon loading I'm getting a System.NullReferenceException
on myLights. I'm assuming that's because my startUpProcedure()
isn't finished yet, hence myLights has not been set. I can't seem to figure out how to wait for the startUpProcedure()
to finish before I run setupUI()
.
To further back that up, if I just run startUpProcedure()
without setupUI()
I get no issues. And then if I run setupUI()
from say a button click it runs just fine.
Clearly I'm missing something here. I just can't seem to find the answer.
So to succinctly put the question: How do I wait for these Async calls to finish so that I can use variables that are depended on their return values?
public sealed partial class MainPage : Page
{
public string appKey;
public string myIP;
public IEnumerable<Light> myLights;
public ILocalHueClient myClient;
public MainPage()
{
this.InitializeComponent();
startUpPocedure();
setupUI();
}
public async Task startUpPocedure()
{
await startUp();
await getLights();
}
public async Task startUp()
{
if (await findBridgeIP())
{
Debug.WriteLine("Bridge Found...");
//Do Actions
}
else
{
//Error!!
Debug.WriteLine("No hue found");
}
Windows.Storage.ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
Windows.Storage.StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
try {
appKey = localSettings.Values["appKey"].ToString();
Debug.WriteLine("appKey loaded: " + appKey);
//Load up
myClient = new LocalHueClient(myIP);
myClient.Initialize(appKey);
}
catch {
Debug.WriteLine("Need to register app");
}
}
async Task getLights()
{
myLights = await myClient.GetLightsAsync();
Debug.WriteLine("Light Count " + myLights.Count());
IEnumerable<string> myLightNames;
List<string> myTempList = new List<string>();
foreach (Light l in myLights)
{
myTempList.Add(l.Name);
Debug.WriteLine(l.Name);
}
myLightNames = myTempList;
comboBox_LightSelect.ItemsSource = myLightNames;
}
private void setupUI()
{
//Populate the Combo Box
IEnumerable<string> myLightNames;
List<string> myTempList = new List<string>();
foreach (Light l in myLights)
{
myTempList.Add(l.Name);
Debug.WriteLine(l.Name);
}
myLightNames = myTempList;
comboBox_LightSelect.ItemsSource = myLightNames;
}
Upvotes: 0
Views: 261
Reputation: 63295
You should leave only InitializeComponent
in the constructor, and move all other logic to Loaded
event handler,
https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.frameworkelement.loaded
Then you can mark that handler as async
, and use await
in it to await on async methods.
Upvotes: 1
Reputation: 2451
Your startUpPocedure
method in MainPage constructor returns Task almost immediately, and because you're not awaiting it, code execution goes to the next line right after that and setupUI
gets called. So when your code starts to enumerate myLights
collection, it's still null because startUpPocedure
and therefore getLights
are still running.
Problem is, you can't await asynchronous methods in a constructor, so in your case solution will be to move both startUpPocedure
and setupUI
to single async method, await them inside this method and call it from constructor similar to this:
public MainPage()
{
this.InitializeComponent();
Startup();
}
private async void Startup()
{
await startUpPocedure();
setupUI();
}
Upvotes: 1