Xamarin Forms - 在选取器选择中使用 Async SelectedIndexChanged
Xamarin Forms - Using Async SelectedIndexChanged in picker selection
我正在为 Android 和 iOS 在 Xamarin Forms 中实现一个应用程序,它与 WebAPI (.NET) 通信以从数据库中检索信息。
在某些时候,我有一个带有多个选择器的屏幕。假设在 picker1 中我们 select 一个国家;在 picker2 的后面我们 select 一个城市。
当一个国家在 picker1 中 selected 时,它会引发一个 SelectedIndexChanged 事件(称为 OnSelectedCountry),该事件调用 WebAPI 从该特定国家/地区检索城市,然后通过以下方式将城市名称绑定到 picker2 picker2.ItemsSource。
这会导致性能不佳,看起来应用程序在执行 OnSelectedCountry 时卡住了,因为 picker1 中的国家/地区列表在 OnSelectedCountry 结束之前不会关闭。
如何在 select 访问一个国家后立即关闭 picker1 中的列表项?或者在上面显示 'loading' 图片。
我已经尝试了 的解决方案,但它们没有用。
代码片段(变量模型省略):
XAML
<Picker x:Name="picker1" Title="Select a country" ItemDisplayBinding="{Binding name}" SelectedIndexChanged="OnSelectedCountry" IsEnabled="False"/>
<Picker x:Name="picker2" Title="Select a city" IsEnabled="False" ItemDisplayBinding="{Binding name}"/>
CS
...
private ObservableCollection<City> _replyCities;
...
async void OnSelectedCountry(object sender, EventArgs e)
{
Country selectedCountry = (Country)picker1.SelectedItem;
string decodedJson = await RestService.GetDataAsync(selectedCountry.name);
Reply reply = Newtonsoft.Json.JsonConvert.DeserializeObject<Reply>(decodedJsonInput);
_replyCities = new ObservableCollection<City>(reply.Cities);
picker2.ItemsSource = _replyCities;
picker2.IsEnabled = true;
}
与 WebAPI 的连接在 RestService 中执行 class 如:
public static async Task<String> GetDataAsync(string queryInput)
{ ... }
你应该运行它在另一个任务中,这样它就不会冻结。
Task.Run(async () => {
try
{
string decodedJson = await RestService.GetDataAsync(selectedCountry.name);
Reply reply = Newtonsoft.Json.JsonConvert.DeserializeObject<Reply>(decodedJsonInput);
_replyCities = new ObservableCollection<City>(reply.Cities);
picker2.ItemsSource = _replyCities;
picker2.IsEnabled = true;
}
catch (System.OperationCanceledException ex)
{
Console.WriteLine($"Text load cancelled: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
});
我强烈建议阅读有关异步任务的内容,因为如果您不熟悉它,它可能会非常棘手。这样做以后会避免很多问题。
此代码使用不同的方法代替您的代码,因为我真的不想编写所有 类 等来测试代码,但希望评论会帮助您将其集成到您的解决方案中.
UPDATE:由于以下解决方案不适用于 Laura 的方法,我怀疑 HTTP 调用未使用 await,因此不是异步的。这是 UI 被阻止的真正原因。
private void Picker1_OnSelectedIndexChanged(object sender, EventArgs e)
{
// add code here to display wait icon if you want
// replace with RestService.GetDataAsync
Task.Delay(5000)
.ContinueWith(t =>
{
// in the case there is an error making the REST call
if (t.IsFaulted)
{
// handle error
return;
}
// t.Result will have the REST response so replace this code with yours
var cities = new ObservableCollection<string>(new[] {"1", "2"});
// Since you're running a new task it is important to make sure all UI
// changes are ran in the main UI thread. This will make sure of that.
Device.BeginInvokeOnMainThread(() =>
{
Picker2.ItemsSource = cities;
Picker2.IsEnabled = true;
// if you displayed wait icon make sure it is disabled here now that the function has completed
});
});
}
```
我正在为 Android 和 iOS 在 Xamarin Forms 中实现一个应用程序,它与 WebAPI (.NET) 通信以从数据库中检索信息。 在某些时候,我有一个带有多个选择器的屏幕。假设在 picker1 中我们 select 一个国家;在 picker2 的后面我们 select 一个城市。
当一个国家在 picker1 中 selected 时,它会引发一个 SelectedIndexChanged 事件(称为 OnSelectedCountry),该事件调用 WebAPI 从该特定国家/地区检索城市,然后通过以下方式将城市名称绑定到 picker2 picker2.ItemsSource。
这会导致性能不佳,看起来应用程序在执行 OnSelectedCountry 时卡住了,因为 picker1 中的国家/地区列表在 OnSelectedCountry 结束之前不会关闭。 如何在 select 访问一个国家后立即关闭 picker1 中的列表项?或者在上面显示 'loading' 图片。
我已经尝试了
代码片段(变量模型省略):
XAML
<Picker x:Name="picker1" Title="Select a country" ItemDisplayBinding="{Binding name}" SelectedIndexChanged="OnSelectedCountry" IsEnabled="False"/>
<Picker x:Name="picker2" Title="Select a city" IsEnabled="False" ItemDisplayBinding="{Binding name}"/>
CS
...
private ObservableCollection<City> _replyCities;
...
async void OnSelectedCountry(object sender, EventArgs e)
{
Country selectedCountry = (Country)picker1.SelectedItem;
string decodedJson = await RestService.GetDataAsync(selectedCountry.name);
Reply reply = Newtonsoft.Json.JsonConvert.DeserializeObject<Reply>(decodedJsonInput);
_replyCities = new ObservableCollection<City>(reply.Cities);
picker2.ItemsSource = _replyCities;
picker2.IsEnabled = true;
}
与 WebAPI 的连接在 RestService 中执行 class 如:
public static async Task<String> GetDataAsync(string queryInput)
{ ... }
你应该运行它在另一个任务中,这样它就不会冻结。
Task.Run(async () => {
try
{
string decodedJson = await RestService.GetDataAsync(selectedCountry.name);
Reply reply = Newtonsoft.Json.JsonConvert.DeserializeObject<Reply>(decodedJsonInput);
_replyCities = new ObservableCollection<City>(reply.Cities);
picker2.ItemsSource = _replyCities;
picker2.IsEnabled = true;
}
catch (System.OperationCanceledException ex)
{
Console.WriteLine($"Text load cancelled: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
});
我强烈建议阅读有关异步任务的内容,因为如果您不熟悉它,它可能会非常棘手。这样做以后会避免很多问题。
此代码使用不同的方法代替您的代码,因为我真的不想编写所有 类 等来测试代码,但希望评论会帮助您将其集成到您的解决方案中.
UPDATE:由于以下解决方案不适用于 Laura 的方法,我怀疑 HTTP 调用未使用 await,因此不是异步的。这是 UI 被阻止的真正原因。
private void Picker1_OnSelectedIndexChanged(object sender, EventArgs e)
{
// add code here to display wait icon if you want
// replace with RestService.GetDataAsync
Task.Delay(5000)
.ContinueWith(t =>
{
// in the case there is an error making the REST call
if (t.IsFaulted)
{
// handle error
return;
}
// t.Result will have the REST response so replace this code with yours
var cities = new ObservableCollection<string>(new[] {"1", "2"});
// Since you're running a new task it is important to make sure all UI
// changes are ran in the main UI thread. This will make sure of that.
Device.BeginInvokeOnMainThread(() =>
{
Picker2.ItemsSource = cities;
Picker2.IsEnabled = true;
// if you displayed wait icon make sure it is disabled here now that the function has completed
});
});
}
```