当绑定到可见性属性时,动画不正确(奇怪)
本文关键字:不正确 动画 奇怪 绑定 可见性 属性 | 更新日期: 2023-09-27 18:04:21
我遇到的问题是,任何时候我通过MVVM模型中的命令改变可见性属性来触发加载动画(例如isBusy = true),动画不会正确播放。在运行过程中,结果是随机的,有时动画非常接近完美,有时它只进行了一半,然后循环。
在任何一种情况下,它总是需要故事板的长度来完成这个行为(例如,它将旋转一个随机的角度,忽略故事板,但总是需要0.5秒来完成)
奇怪的是,如果我从构造函数触发isBusy,动画会完美地工作,但如果我通过commandExecute调用它,它就会中断。下面是代码示例和我的XAML。
<Grid x:Name="LoadingGrid" Visibility="{Binding isBusy, Converter={StaticResource BooleanToVisibilityConverter}, Mode=TwoWay}" Grid.RowSpan="2">
<LoadingViews:LoadingView x:Name="LoadingControl" />
</Grid>
c#: public StoreSearchViewModel(MainViewModel mainViewModel)
{
this.mainViewModel = mainViewModel;
mainViewModel.LogUsage("Store Search");
searchResultsCommand = new DelegateCommand(SearchResultsCommandExecute);
storeSearchCommand = new DelegateCommand<object>(SetBusy, CanStoreSearchCommandExecute);
CloseWindowCommand = new DelegateCommand(CloseWindowExecute);
Setup();
}
private void SetBusy(object obj)
{
isBusy = true;
}
private bool _isBusy;
public bool isBusy
{
get { return _isBusy; }
set { _isBusy= value; OnPropertyChanged("isBusy"); }
}
以上代码将导致加载动画出现故障,动画位于网格中,其可见性由isBusy决定,并由视图中的命令触发。触发的命令为storeSearchCommand。
然而,下面的代码将产生良好的动画。
private void Setup()
{
//create view models
_storeSearchResultsViewModel = new StoreSearchResultsViewModel(this);
//set default selection to the dashboard
isStoreSearchResultsSelected = true;
SearchResultsCommandExecute();
SetBusy();
}
请注意,"对象obj"只是我传递所需的参数来测试代码。请忽略与传递对象的任何不一致。
我绞尽脑汁想了一段时间,就是想不明白。
这是因为在UI线程中使用了一些长时间运行的代码。让我们考虑一个例子:
- 你点击了某个按钮。 这个按钮执行一些命令。
- 该命令默认在UI线程中运行。
- 该命令将isBusy = true设置为"长"工作。
在UI中,您将看到只有当我们的命令结束其长代码运行时才开始一些动画(绑定到isBusy属性)。这只是一个例子。
所以,你应该使用异步调用。它的意思是,设置isBusy = true,在单独的线程中执行一些代码。你几乎可以立即看到isBusy属性的变化。下面是一个例子:
isBusy = true;
Task.Factory.StartNew(() =>
{
//do some code. It is a new thread
}).ContinueWith((x) =>
{
//do some code after previous task is done. This code runs in the UI thread
isBusy = false;
}, TaskScheduler.FromCurrentSynchronizationContext());
通过在本地构建动画而不是引用包含动画的不同视图来修复自己的问题。
:
<Grid x:Name="LoadingGrid" Visibility="{Binding isBusy, Converter={StaticResource BooleanToVisibilityConverter}, Mode=TwoWay}" Grid.RowSpan="2">
<LoadingViews:LoadingView x:Name="LoadingControl" />
</Grid>
:
<Window.Resources>
<Storyboard x:Key="Storyboard1" RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)">
<SplineDoubleKeyFrame KeyTime="00:00:01" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource Storyboard1}"/>
</EventTrigger>
</Window.Triggers>
<ContentControl x:Name="CurrentViewContentControl" Content="{Binding currentView, Mode=TwoWay}" Background="#FFDC00FF" Grid.Row="1"/>
<Grid x:Name="LoadingGrid" Visibility="{Binding isBusy, Converter={StaticResource BooleanToVisibilityConverter}}" Grid.RowSpan="2" d:IsHidden="True">
<Grid.Background>
<RadialGradientBrush>
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="#BF000000" Offset="1"/>
</RadialGradientBrush>
</Grid.Background>
<Grid Margin="446,285">
<Ellipse x:Name="ellipse" StrokeThickness="6" RenderTransformOrigin="0.5,0.5">
<Ellipse.Effect>
<DropShadowEffect ShadowDepth="3"/>
</Ellipse.Effect>
<Ellipse.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
</TransformGroup>
</Ellipse.RenderTransform>
<Ellipse.Stroke>
<LinearGradientBrush EndPoint="0.445,0.997" StartPoint="0.555,0.003">
<GradientStop Color="#FF0244D1"/>
<GradientStop Color="#00000000" Offset="0.313"/>
<GradientStop Color="#FF0244D1" Offset="0.063"/>
</LinearGradientBrush>
</Ellipse.Stroke>
</Ellipse>
</Grid>
</Grid>
可能不是很干净,因为我在项目的其他地方建立了这个确切的动画,但至少它现在动画正确。