基于视图模型属性运行动画?
我试图真正遵守 MVVM 模式,因此我很难弄清楚如何在每次设置视图模型上的某些属性(通过绑定)时在视图上触发动画。
在我的 MainWindow.xaml 中,我有一个 ContentPresenter,并且我将该演示者的内容绑定到向用户显示的当前视图的视图模型。例如,当用户第一次运行应用程序时,ContentPresenter将绑定到StartViewModel。例如,如果用户单击一个按钮将他/她带到不同的屏幕来运行研究,则 ContentPresenter 需要绑定到 StudyViewModel 以便可以显示 StudyView 屏幕。然后,如果用户希望返回到起始屏幕,此时的 ContentPresenter 将再次绑定到应用程序启动时创建的 StartViewModel。它看起来像这样:
<Window
...
<Grid
...
<ContentControl
x:Name="CurrentContentView"
Width="Auto"
Grid.Row="2"
Content="{Binding CurrentContentViewModel}" />
</Grid>
</Window>
CurrentContentViewModel 的类型为“ViewModelBase”,它是我为所有实现 INotifyPropertyChanged 的视图模型创建的基类。我在幕后保留了 ViewModelBase 的 ObservableCollection,每次发生一些导致用户转到新屏幕的事件时,我都会在该集合中查找适当的视图模型,并将 CurrentContentViewModel 设置为该视图模型。如果我的集合中尚不存在它,我会先将其添加到集合中,然后将 CurrentContentViewModel 设置为该视图模型。
我的问题是:如何使我的视图在每次显示视图时运行“介绍”动画?我无法将故事板设置为每次在 ContentPresenter 中简单加载视图时都运行,因为我还想在用户返回到应用程序生命周期中先前已加载的视图时再次运行动画。最重要的是,我的视图是“愚蠢的”,并且不知道它再次显示在 MainWindow 中,因为我的 MainWindow 上的 ContentPresenter 绑定到视图模型而不是视图(所以它几乎就像 VIEW MODEL 会每次绑定到内容呈现器时都必须触发某个事件,并且视图必须响应该事件)。有没有办法可以在我的 xaml 标记中声明某种原始类型(例如布尔值),该类型可以绑定到我的视图模型中的布尔值?
预先感谢任何人可能有的任何想法,如果这个问题已经被问到,我们很抱歉。我发现了一些类似的帖子,但没有什么像我想做的那样。
更新 - 找到解决方案
事实证明(至少在我目前的实现中),这是一个比我意识到的更容易解决的问题。如果您使用数据模板模式构建视图(即在资源字典中为每个视图模型定义一个数据模板,然后将该资源字典添加到 app.xaml 文件的资源列表中),则每次都会重新创建视图它已从内容控件中删除。就我而言,每次我将内容控件的内容绑定到与前一个不同的视图模型时,为该视图模型“构建”的视图实际上都会被释放。然后,当视图模型再次重新分配给内容控件时,视图将重新构建。因此,我可以将屏幕介绍动画放在视图 XAML 的 Loaded 事件中,将屏幕结束动画放在视图 XAML 的 UnLoaded 事件中。
我想分享这一点,以防其他人面临类似的问题 - 我不知道使用数据模板来显示视图模型的视图的这种类型的行为。
I'm trying to really adhere to the MVVM pattern, and as a result I'm having difficulty figuring out how I can get an animation on a view to fire every time some property on a view model is set (via binding).
In my MainWindow.xaml I have a ContentPresenter and I am binding the content of that presenter to the view model of the current view that's displayed to the user. For example, when the user first runs the application, ContentPresenter will be bound to StartViewModel. If the user then clicks on a button that takes him/her to a different screen to run a study, for example, the ContentPresenter needs to be bound to StudyViewModel so that the StudyView screen can be displayed. Then, if the user wishes to go back to the starting screen, the ContentPresenter at that point will once again be bound to the StartViewModel that was created when the application launched. It looks something like this:
<Window
...
<Grid
...
<ContentControl
x:Name="CurrentContentView"
Width="Auto"
Grid.Row="2"
Content="{Binding CurrentContentViewModel}" />
</Grid>
</Window>
CurrentContentViewModel is of type "ViewModelBase," which is a base class I've created for all view models that implements INotifyPropertyChanged. I keep an ObservableCollection of ViewModelBase behind the scenes and every time some event happens that should cause the user to go to a new screen, I look for the appropriate view model in that collection and set CurrentContentViewModel to that view model. If it doesn't exist yet in my collection, I add it to the collection first and then set CurrentContentViewModel to that view model.
My question is: how can I cause my views to run an "intro" animation every time the view is displayed? I can't set the storyboard to be run every time a view is simply loaded within my ContentPresenter, because I also want to run the animation again when the user comes back to a view that was already loaded previously in the application's lifetime. On top of that, my view is "dumb" and has no idea that it has once again been displayed in the MainWindow because the ContentPresenter on my MainWindow is bound to a view model rather than a view (so it's almost like the VIEW MODEL would have to fire some event every time it gets bound to the content presenter and the view would have to respond to that event). Is there a way I can declare some sort of primitive type like a Boolean in my xaml markup that can bind to a boolean in my view model?
Thanks in advance for any ideas anyone might have, and sorry if this question has already been asked. I found some similar posts, but nothing quite like what I'm wanting to do.
UPDATE - SOLUTION FOUND
As it turns out (at least with the implementation I currently have), this was a much easier problem to solve than I realized. If you are building your views using the data template pattern (i.e. define a data template for each of your view models in a resource dictionary and then add that resource dictionary to the app.xaml file's list of resources), the view gets recreated every time it is removed from a content control. In my case, each time I bind my Content Control's Content to a different view model than the previous one, the view that was "built" for that view model actually gets disposed. Then, when the view model is once again reassigned to the Content Control, the view gets built all over again. Therefore, I am able to put my screen intro animations in the Loaded event of the view's XAML and my screen outro animations in the UnLoaded event of the view's XAML.
I wanted to share this in case anyone else is facing a similar problem - I was unaware of this type of behavior using data templates to display the view for a view model.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这取决于您想要什么动画。例如,如果您想滑出当前视图,然后滑入下一个视图,您只需让控制视图模型引发两个事件:
CurrentContentViewModelChanging
和CurrentContentViewModelChanged
。您的视图可以根据这些事件触发适当的动画。但是,如果您的动画需要与旧视图同时显示新视图,事情就会变得有点棘手。您要么需要与新视图同时公开旧视图,要么需要您的视图在新视图被替换之前拍摄旧视图的快照。同样,您应该能够使用相同的事件来实现这一目标。
It rather depends on what animation you're after. For example, if you want to slide out the current view and then slide in the next, you could simply have your controlling view model raise two events:
CurrentContentViewModelChanging
andCurrentContentViewModelChanged
. Your view can trigger appropriate animations based on those events.However, if your animation needs to show the new view at the same time as the old view, things get a little trickier. You either need to expose the old view at the same time as the new, or you need your view to take a snapshot of the old view just before the new view is substituted in. Again, you should be able to use the same events to achieve this.