C# 当opacity属性发生更改并随后发生等待方法调用时,UI不会更新
当我的应用程序启动时,它会调用一个方法来显示进度指示器。此方法将内容区域的不透明度设置为0,将进度条的不透明度设置为1。这是一个弹出控件 进度指示器的弹出式xaml:C# 当opacity属性发生更改并随后发生等待方法调用时,UI不会更新,c#,windows-phone-8,windows-phone,async-await,C#,Windows Phone 8,Windows Phone,Async Await,当我的应用程序启动时,它会调用一个方法来显示进度指示器。此方法将内容区域的不透明度设置为0,将进度条的不透明度设置为1。这是一个弹出控件 进度指示器的弹出式xaml: <Popup x:Name="loadingOverlay" IsOpen="{Binding IsProgressBarOpen}" Margin="10,-100,0,0" Opacity="{Binding OpacityProgre
<Popup
x:Name="loadingOverlay"
IsOpen="{Binding IsProgressBarOpen}"
Margin="10,-100,0,0"
Opacity="{Binding OpacityProgressBar}"
VerticalAlignment="Center" HorizontalAlignment="Center">
<Border
x:Name="loadingOverlayPanel"
BorderBrush="{StaticResource PhoneAccentBrush}" >
<StackPanel
VerticalAlignment="Center">
<telerik:RadBusyIndicator
x:Name="LoadingVisualAngleTransform"
Content=" "
Foreground="{Binding ActiveTheme.ForegroundAlt1, Source={StaticResource ThemeController}}"
FontSize="18"
FontWeight="Bold"
IndicatorAnimationStyle="{StaticResource ColorWheelIndicatorAnimation}"
IsRunning="True"
Opacity="{Binding OpacityProgressBar}"
VerticalAlignment="Center">
</telerik:RadBusyIndicator>
</StackPanel>
</Border>
</Popup>
我遇到的问题是,如果showProgressBar方法调用后面跟着wait方法调用,则进度指示器没有显示
在下面的代码中,我首先调用进度条显示,然后请求获取手机的位置坐标。接下来的代码应该只在收到坐标后运行,这就是我使用wait关键字的原因。我假设showProgressBar和setLocationCoordinates调用之间的短时间没有给UI足够的更新时间?奇怪的是,在调试时,一旦获取了位置坐标,我注意到进度指示器仍然没有出现
// Progress bar should be shown as weather is being fetched.
App.ViewModel.showProgressBar();
// Must retrieve GPS location details with the status of
// the requested recorded into a variable.
// Have 3 attemps cause sometimes first attempt fails.
string status = "";
int attempts = 1;
while( status != "success" )
{
status = await App.ViewModel.GPS.setLocationCoordinates();
if (status == "success")
{
App.ViewModel.GPS.setLocationDetails();
break;
}
attempts++;
if (attempts > 3)
break;
}
我应该补充一点,如果showProgressBar后面没有wait方法,则不会出现此问题
更新:在我的页面中添加了xaml,以向Pantelis表明我实际上一直在使用他的推荐。我有一个没有不透明度绑定的网格,弹出控件和轴控件作为兄弟控件,每个控件都绑定了不透明度。如果这不是你的建议,请告诉我
<!-- RESOURCES -->
<phone:PhoneApplicationPage.Resources>
<Style x:Key="ColorWheelIndicatorAnimation" TargetType="telerikBusy:BusyIndicatorAnimation">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel
x:Name="PART_LayoutRoot">
<StackPanel.Resources>
<Storyboard
x:Name="PART_Animation"
RepeatBehavior="Forever">
<DoubleAnimation
Storyboard.TargetName="progressBarIcon1"
Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:1"
AutoReverse="True"/>
<DoubleAnimation
Storyboard.TargetName="progressBarIcon2"
Storyboard.TargetProperty="Opacity"
From="0.0" To="1.0" Duration="0:0:1"
AutoReverse="True"/>
</Storyboard>
</StackPanel.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
x:Name="progressBarIcon1"
FontSize="48"
FontFamily="/Assets/Fonts/WeatherIcons-Regular.otf#Weather Icons"
Foreground="{Binding ActiveTheme.Foreground, Source={StaticResource ThemeController}}"
Text=""
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<TextBlock Grid.Row="0"
x:Name="progressBarIcon2"
FontSize="48"
FontFamily="/Assets/Fonts/WeatherIcons-Regular.otf#Weather Icons"
Foreground="{Binding ActiveTheme.Foreground, Source={StaticResource ThemeController}}"
Text=""
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</phone:PhoneApplicationPage.Resources>
<!--LAYOUT CONTAINER -->
<Grid
x:Name="LayoutRoot"
Background="{Binding ActiveTheme.Background, Source={StaticResource ThemeController}}"
CacheMode="BitmapCache">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MinHeight="800"/>
</Grid.RowDefinitions>
<!-- LOADING OVERLAY -->
<Popup
x:Name="loadingOverlay"
IsOpen="{Binding IsProgressBarOpen}"
Margin="10,-100,0,0"
Opacity="{Binding OpacityProgressBar}"
VerticalAlignment="Center" HorizontalAlignment="Center">
<Border
x:Name="loadingOverlayPanel"
BorderBrush="{StaticResource PhoneAccentBrush}" >
<StackPanel
VerticalAlignment="Center">
<telerik:RadBusyIndicator
x:Name="LoadingVisualAngleTransform"
Content=" "
Foreground="{Binding ActiveTheme.ForegroundAlt1, Source={StaticResource ThemeController}}"
FontSize="18"
FontWeight="Bold"
IndicatorAnimationStyle="{StaticResource ColorWheelIndicatorAnimation}"
IsRunning="True"
Opacity="{Binding OpacityProgressBar}"
VerticalAlignment="Center">
</telerik:RadBusyIndicator>
</StackPanel>
</Border>
</Popup>
<!-- TITLE BAR START -->
<StackPanel
Grid.Row="0"
Margin="0,17,0,0"
Opacity="{Binding OpacityContent}"
x:Name="titleBar">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<TextBlock
FontWeight="Bold"
FontSize="18"
Foreground="{Binding ActiveTheme.ForegroundAlt1, Source={StaticResource ThemeController}}"
Grid.Row="0"
Grid.Column="1"
Margin="0,0,0,0"
Opacity="0.5"
Tap="Title_Tap"
Text="title"/>
</Grid>
</StackPanel>
<!-- TITLE BAR END -->
<!-- PIVOTS -->
<!-- Taken from: http://social.msdn.microsoft.com/Forums/wpapps/en-us/1baf74fa-0ddd-4226-a02d-a7fc9f80374d/pivot-static-header-like-twitter-app?forum=wpdevelop-->
<controls:Pivot
x:Name="MainPivot"
Background="{Binding ActiveTheme.Background, Source={StaticResource ThemeController}}"
Foreground="{Binding ActiveTheme.Foreground, Source={StaticResource ThemeController}}"
Margin="0,50,0,0"
Opacity="{Binding OpacityContent}"
Padding="0,0,0,0"
SelectionChanged="Pivot_SelectionChanged">
// Pivots defined here, but none of them use OpacityContent
</controls:Pivot>
</Grid>
更新2:以下是在调用showProgressBar方法后调用的异步方法:
public async Task<string> setLocationCoordinates()
{
// Get the latitude and longitude.
status = await this.setCoordinates();
if (status == "success")
{
this.CoordinatesFetched = true;
}
this.CoordinatesFetched = false;
return status;
}
public async Task<string> setCoordinates()
{
// Need to initialise the tracking mechanism.
Geolocator geolocator = new Geolocator();
// Must determine if GPS is on/off.
if (geolocator.LocationStatus == PositionStatus.Disabled)
{
Status = false;
// If off, get out.
return "gps off";
}
else
Status = true;
// Setup the desired accuracy in meters for data returned from the location service.
geolocator.DesiredAccuracyInMeters = 50;
try
{
// Taken from: http://bernhardelbl.wordpress.com/2013/11/26/geolocator-getgeopositionasync-with-correct-timeout/
// Because sometimes GetGeopositionAsync does not return. So you need to add a timeout procedure by your self.
// get the async task
var asyncResult = geolocator.GetGeopositionAsync();
var task = asyncResult.AsTask();
// add a race condition - task vs timeout task
var readyTask = await Task.WhenAny(task, Task.Delay(10000));
if (readyTask != task) // timeout wins
{
return "error";
//throw new TimeoutException();
}
// position found within timeout
Geoposition geoposition = await task;
// Retrieve latitude and longitude.
Latitude = Convert.ToDouble(geoposition.Coordinate.Latitude.ToString("0.0000000000000"));
Longitude = Convert.ToDouble(geoposition.Coordinate.Longitude.ToString("0.0000000000000"));
// Update the 'Current Location' db record.
var query = from LocationDbRecord location in App.ViewModel.LocationDb.Locations
where location.LocationId == 1
select location;
foreach (LocationDbRecord l in query)
{
l.Latitude = Latitude.ToString();
l.Longitude = Longitude.ToString();
}
// Submit the changes to the database.
try
{
App.ViewModel.LocationDb.SubmitChanges();
}
catch (Exception ee)
{
Console.WriteLine(ee);
}
return "success";
}
// If there's an error, may be because the ID_CAP_LOCATION in the app manifest wasn't include.
// Alternatively, may be because the user hasn't turned on the Location Services.
catch (Exception ex)
{
if ((uint)ex.HResult == 0x80004004)
{
return "gps off";
}
else
{
// Something else happened during the acquisition of the location.
// Return generic error message.
return "error";
}
}
}
这是在获取坐标后调用的方法,以获取位置的名称:
public void setLocationDetails()
{
// Must perform reverse geocoding i.e. get location from latitude/longitude.
ReverseGeocodeQuery query = new ReverseGeocodeQuery()
{
GeoCoordinate = new GeoCoordinate(Latitude, Longitude)
};
query.QueryCompleted += query_QueryCompleted;
query.QueryAsync();
}
/**
* Event called when the reverse geocode call returns a location result.
**/
void query_QueryCompleted(object sender, QueryCompletedEventArgs<IList<MapLocation>> e)
{
foreach (var item in e.Result)
{
Location = item.Information.Address.City;
// Update the 'Current Location' db record.
var query = from LocationDbRecord location in App.ViewModel.LocationDb.Locations
where location.LocationId == 1
select location;
foreach (LocationDbRecord l in query)
{
l.Country = "Somewhere in " + Location;
}
// Submit the changes to the database.
try
{
App.ViewModel.LocationDb.SubmitChanges();
}
catch (Exception ee)
{
Console.WriteLine(ee);
}
// Need to passively update the current location's general location in the locations Db.
this.updateCurrentLocationLiveTileDbRecord(item.Information.Address.City, item.Information.Address.Country, item.GeoCoordinate.Latitude, item.GeoCoordinate.Longitude);
return;
}
}
这是因为任何嵌套元素的不透明度都是由它们所包含的元素继承的。您必须在单个单元格网格中获得弹出窗口和stackpanel(繁忙指示器所在的位置),以停止不透明度继承
<Grid>
<Popup Opacity="{Binding ...}"/>
<ProgressBar Opacity="{Binding ...}"/>
</Grid>
尝试将setLocationCoordinates方法更改为:
public Task<string> setLocationCoordinates() {
return Task.Run(async delegate() {
// Get the latitude and longitude.
status = await this.setCoordinates();
this.CoordinatesFetched = status == "success";
return status;
});
}
当我编辑这段代码时,我注意到您总是将CoordinationsFetched设置为false。这是有意的吗?还是我错过了什么?在我的代码中,它取决于status的值
另外,正如@Pantelis所说的,只要有可能,你应该使用可见性而不是不透明度来显示/隐藏控件,这大约是99%的情况。好的,所以我通过添加以下内容来实现这一点: this.loadingOverlay.IsOpen=true 调用setLocationCoordinates之前,然后在调用之后手动将其设置为false,因为showProgressBar中的this.IsProgressBarOpen=false行似乎没有更新它。出于我现在正在研究的某些原因,正在加载Overlay的IsOpen属性。如果有人知道原因,请告诉我,因为我很好奇
谢谢大家的建议和建议。我会考虑的。您是否尝试过单步调试SetLocationCoordinates方法以查看是否返回?放置等待任务。在showProgressBar之后立即屈服,并查看发生了什么。是的,SetLocationCoordinates方法工作正常,并且在获取坐标后正确返回成功。发生的情况是,只有一个空白屏幕,然后突然出现天气,即OpacityContent变为1。我添加了等待任务。showProgressBar之后立即放弃,但不幸的是,这似乎没有任何区别。异步方法的代码是什么?如果这个方法不是真正的异步的,它会导致这种行为。原来的帖子现在用相关的xaml更新了。如果我错了或者我误解了你的建议,请纠正我。我不确定我是否完全理解你的布局逻辑。在获取位置数据时,您正在显示忙指示器。完成后,您希望向UI的其余部分显示一个神秘的标题栏,其中有4列,只有一个子项和pivot控件。对吗?您是否尝试过更改控件的可见性属性?这是在UI上显示/隐藏元素的首选方式。同样像@yasen提到的,异步方法的实现是什么?我想IsProgressBarOpen是可以观察到的?也就是说,它要么是DependencyProperty,要么在设置时从INotifyPropertyChanged接口引发PropertyChanged事件?它是可观察的,是的。
public Task<string> setLocationCoordinates() {
return Task.Run(async delegate() {
// Get the latitude and longitude.
status = await this.setCoordinates();
this.CoordinatesFetched = status == "success";
return status;
});
}