无法让(相当简单)WPF 动画工作

发布于 2024-12-04 17:40:05 字数 3743 浏览 0 评论 0原文

我正在尝试在 WPF 中获取动画消息面板,但到目前为止尚未取得成功。

情况是这样的:

  1. 我有一个带有 StackPanel 的用户控件,其中包含绑定到控件的视图模型对象 (ViewModel.Messages) 中的(可观察)集合的 ItemsControl )。
  2. 当我需要向用户展示消息时,我会将这些消息(作为 MessageVM 实例)添加到可观察集合中。

ItemsControl 的可见性绑定到名为 ViewModel.CountVisibleMessages 的整数属性,并且有一个转换器负责将 0 转换为 Visibility.Hidden 以及Visibility.Visible 的正值。

这很好用。当消息添加到集合中时,StackPanel 自动变得可见,并且当用户(或计时器)删除最后一条消息时,它会隐藏。当然,StackPanel 的高度会自动调整以适应所有消息。

为了让一切看起来更好,我更希望 StackPanel 使用运行 300 毫秒的动画来调整自身大小。 (最终我也希望它能够加速和减速,但这超出了我现在的野心。

我已经实验了几个小时,但我觉得我还没有接近。 下面是我目前的(甚至还没有接近工作)XAML:(

<StackPanel Orientation="Vertical" 
                VerticalAlignment="Top" 
                Visibility="{Binding CountVisibleMessages, Converter={StaticResource IntToVisibility}}"
                Height="Auto"
                Background="{DynamicResource HmiBackColorLightBrush}">
        <StackPanel.Style>
            <Style>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding CountVisibleMessagesChanged}" Value="True" ><!-- I suppose I shopuld've used a Routed Event here but I just needed to get it triggered -->
                        <DataTrigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation
                                        Storyboard.TargetProperty="Margin.Bottom"
                                        From="100" <!-- Just a made up value to test the concept -->
                                        To="0"
                                        Duration="0:0:0:3"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </DataTrigger.EnterActions>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </StackPanel.Style>
        <ItemsControl ItemsSource="{Binding Messages}" >
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Vertical" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border Style="{DynamicResource Message}">
                        <Grid >
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="15" />
                            </Grid.ColumnDefinitions>
                            <TextBlock Grid.Column="0" Text="{Binding Text}" Margin="3" Style="{Binding MessageType, Converter={StaticResource MessageTypeToStyle}, ConverterParameter={x:Type TextBlock}}" /> <!-- using dynamic styling here -->
                            <RadioButton Grid.Column="1" Style="{DynamicResource HmiCloseMessageButton}" IsChecked="{Binding IsVisible, Converter={StaticResource BoolToVisibility}, ConverterParameter=true}" />
                        </Grid>
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </StackPanel>

我确实意识到上面的 XAML 不会让 StackPanel 缓慢地自动调整大小。这只是一个让任何事情发生的实验)。

我认为这不会太困难(这是许多程序中相当标准的 UI 行为),因此如果有人能够为我指出正确的方向,我将不胜感激。

干杯

I'm trying to get a message panel animated in WPF but has so far achieved no success.

This is the situation:

  1. I have a user control with a StackPanel containing an ItemsControl bound to an (observable) collection in the control's View Model object (ViewModel.Messages).
  2. When I need to present the user with messages I ad those (as MessageVM instances) to the observable collection.

The ItemsControl's visibility is bound to an integer property called ViewModel.CountVisibleMessages and there's a converter taking care of translating 0 to Visibility.Hidden and positive values to Visibility.Visible.

This works just fine. When a message gets added to the collection the StackPanel automatically becomes visible and as the user (or a timer) removes the last message it gets hidden. The StackPanel height is automatically adjusted to fit all messages of course.

To make everything look nicer I would prefer it if the StackPanel resized itself using an animation running for, say, 300 ms. (Ultimately I would also like it to accelerate and deccelerate but that's beyond my ambition right now.

I have experimented for a few hours now but I feel I'm not even close.
Below is my current (not even close to working) XAML at the moment:

<StackPanel Orientation="Vertical" 
                VerticalAlignment="Top" 
                Visibility="{Binding CountVisibleMessages, Converter={StaticResource IntToVisibility}}"
                Height="Auto"
                Background="{DynamicResource HmiBackColorLightBrush}">
        <StackPanel.Style>
            <Style>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding CountVisibleMessagesChanged}" Value="True" ><!-- I suppose I shopuld've used a Routed Event here but I just needed to get it triggered -->
                        <DataTrigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation
                                        Storyboard.TargetProperty="Margin.Bottom"
                                        From="100" <!-- Just a made up value to test the concept -->
                                        To="0"
                                        Duration="0:0:0:3"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </DataTrigger.EnterActions>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </StackPanel.Style>
        <ItemsControl ItemsSource="{Binding Messages}" >
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Vertical" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border Style="{DynamicResource Message}">
                        <Grid >
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="15" />
                            </Grid.ColumnDefinitions>
                            <TextBlock Grid.Column="0" Text="{Binding Text}" Margin="3" Style="{Binding MessageType, Converter={StaticResource MessageTypeToStyle}, ConverterParameter={x:Type TextBlock}}" /> <!-- using dynamic styling here -->
                            <RadioButton Grid.Column="1" Style="{DynamicResource HmiCloseMessageButton}" IsChecked="{Binding IsVisible, Converter={StaticResource BoolToVisibility}, ConverterParameter=true}" />
                        </Grid>
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </StackPanel>

(I do realize the above XAML won't get the StackPanel to auto-resize slowly. It's just an experiment to get anything happening).

This can't be too difficult I suppose (it's a pretty standard UI behavior in many programs) so I'd appreciate it if anyone could point me in the right directions.

Cheers

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

尽揽少女心 2024-12-11 17:40:05

您确定要调整下边距吗?堆栈面板 VerticalAlignment 位于顶部。如果您想更改高度,请将 StoryBoard 属性绑定到高度。您知道您的 StoryBoard 是否正在启动吗?

Are you sure you want to adjust the bottom margin? The stack panel VerticalAlignment is top. If you want to change the Height then bind your StoryBoard Property to Height. Do you know if your StoryBoard is firing?

花期渐远 2024-12-11 17:40:05

关键点是 ExitActions 对于基于 EnterAction 的 dataTrigger 动画是必需的。所以以下内容似乎适用于我的情况......

<StackPanel Grid.Row="0" Height="100" x:Name="MyGrid">
   <StackPanel.Style>
      <Style>
         <Style.Triggers>
            <DataTrigger
               Binding="{Binding ElementName=MyCheckBox,
                                 Path=IsChecked}" Value="True">
               <DataTrigger.EnterActions>
                  <BeginStoryboard>
                     <Storyboard>
                        <DoubleAnimation 
                           Storyboard.Target="{Binding
                             RelativeSource={RelativeSource
                               AncestorType={x:Type 
                                 StackPanel}},
                             BindsDirectlyToSource=True}"
                           Storyboard.TargetProperty="Height"
                           From="100" 
                           To="200"
                           Duration="0:0:1"/>
                     </Storyboard>
                  </BeginStoryboard>
               </DataTrigger.EnterActions>
               <DataTrigger.ExitActions>
                  <BeginStoryboard>
                     <Storyboard>
                        <DoubleAnimation 
                           Storyboard.Target="{Binding
                              RelativeSource={RelativeSource
                                 AncestorType={x:Type 
                                   StackPanel}},
                              BindsDirectlyToSource=True}"
                              Storyboard.TargetProperty="Height"
                              To="100"
                              Duration="0:0:1"/>
                     </Storyboard>
                  </BeginStoryboard>
               </DataTrigger.ExitActions>
            </DataTrigger>
         </Style.Triggers>
      </Style>
   </StackPanel.Style>
 </StackPanel>
 <CheckBox x:Name="MyCheckBox" Grid.Row="1" />

让我知道这是否有帮助。

The key point is ExitActions are necessary for EnterAction based dataTrigger animations. So the following seems to be working in my case ....

<StackPanel Grid.Row="0" Height="100" x:Name="MyGrid">
   <StackPanel.Style>
      <Style>
         <Style.Triggers>
            <DataTrigger
               Binding="{Binding ElementName=MyCheckBox,
                                 Path=IsChecked}" Value="True">
               <DataTrigger.EnterActions>
                  <BeginStoryboard>
                     <Storyboard>
                        <DoubleAnimation 
                           Storyboard.Target="{Binding
                             RelativeSource={RelativeSource
                               AncestorType={x:Type 
                                 StackPanel}},
                             BindsDirectlyToSource=True}"
                           Storyboard.TargetProperty="Height"
                           From="100" 
                           To="200"
                           Duration="0:0:1"/>
                     </Storyboard>
                  </BeginStoryboard>
               </DataTrigger.EnterActions>
               <DataTrigger.ExitActions>
                  <BeginStoryboard>
                     <Storyboard>
                        <DoubleAnimation 
                           Storyboard.Target="{Binding
                              RelativeSource={RelativeSource
                                 AncestorType={x:Type 
                                   StackPanel}},
                              BindsDirectlyToSource=True}"
                              Storyboard.TargetProperty="Height"
                              To="100"
                              Duration="0:0:1"/>
                     </Storyboard>
                  </BeginStoryboard>
               </DataTrigger.ExitActions>
            </DataTrigger>
         </Style.Triggers>
      </Style>
   </StackPanel.Style>
 </StackPanel>
 <CheckBox x:Name="MyCheckBox" Grid.Row="1" />

Let me know if this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文