绑定到可见性属性时动画不正确(奇数)
Posted
技术标签:
【中文标题】绑定到可见性属性时动画不正确(奇数)【英文标题】:Animation not correct when bound to a visibility property(Oddity) 【发布时间】:2015-10-13 00:44:36 【问题描述】:我遇到的问题是,每当我通过 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();
请注意,“object obj”只是我传递所需的参数来测试代码。请忽略与传递的对象的任何不一致。
我已经为此绞尽脑汁了一段时间,根本无法弄清楚。
【问题讨论】:
请发布 CanStoreSearchCommandExecute 的代码。 CanStoreSearchCommandExecute 仅检查以确保传递的对象符合某些要求。它不应该以任何方式影响加载动画。 (它会彻底阻止代码运行,如果验证失败,则根本不会播放动画) 好的,你能发布一个测试项目的 zip 文件,以便我可以尝试在我的机器上获得相同的结果吗?我在这里尝试过,但没有运气。 【参考方案1】:这是因为在 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());
【讨论】:
我还应该指出,为了测试代码,我唯一要做的就是将 isBusy 的属性设置为 true。我的一些代码流是“触发命令-> 将 isBusy 设置为 true”,之后没有代码。实际上,它应该触发并永久停止动画,因为我从不将其设置回 false 或在后台运行任何代码。【参考方案2】:通过在本地构建动画而不是引用包含动画的不同视图,我自己解决了这个问题。
之前:
<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>
可能不是很干净,因为我在项目的其他地方构建了这个确切的动画,但至少它现在可以正确动画了。
【讨论】:
以上是关于绑定到可见性属性时动画不正确(奇数)的主要内容,如果未能解决你的问题,请参考以下文章