【问题标题】:WPF: DoubleAnimation "To" property binding timingsWPF:DoubleAnimation“到”属性绑定时间
【发布时间】:2019-04-27 20:14:58
【问题描述】:

我正在尝试制作一个Button,它使用该面板的Height 属性上的动画来打开/关闭某个面板。

方法是将动画的“To”值绑定到 viewmodel 属性,该属性在按钮的命令中更改。

我想它应该像这样工作:

  1. Button被点击
  2. 视图模型检查其“IsOpened”属性
  3. 如果为True,则vm设置为False,TargetHeight为0;如果为 False,则 vm 将其设置为 True,TargetHeight 设置为 128
  4. 动画触发并将高度更改为所需值

但实际上它是这样工作的:

  1. Button被点击
  2. 动画触发并将 Height 更改为之前设置的值
  3. viewmodel 检查其“IsOpened”属性并更改其值

因此,viewmodel 设置的目标高度不是针对当前单击按钮,而是针对未来的下一次。

我应该改变什么来使它正确? 已经尝试设置BeginTime 属性来延迟动画,不幸的是它没有帮助。

代码如下:

XAML

<Button Command="{Binding SwitchBottomPanel}">
        <Button.Triggers>
            <EventTrigger RoutedEvent="Button.Click">
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetName="BottomPanel"
                                         Storyboard.TargetProperty="Height"
                                         To="{Binding TargetHeight}"
                                         Duration="0:0:0.3"
                                         BeginTime="0:0:0.3"/>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </Button.Triggers>
    </Button>

视图模型类:

    class MainViewModel : ViewModelBase
{
    private double _targetHeight;
    private bool _isBottomOpened;
    private double _bottomHeightBig = 128;

    public double TargetHeight
    {
        get { return _targetHeight; }
        set
        {
            _targetHeight = value;
            RaisePropertyChanged(nameof(TargetHeight));
        }
    }

    public bool IsBottomOpened
    {
        get { return _isBottomOpened; }
        set
        {
            _isBottomOpened = value;

            if (value == true) TargetHeight = _bottomHeightBig;
            else TargetHeight = 0;

            RaisePropertyChanged(nameof(IsBottomOpened));
        }
    }

    public ICommand SwitchBottomPanel { get; set; }

    public MainViewModel()
    {
        SwitchBottomPanel = new DelegateCommand(() => IsBottomOpened = !IsBottomOpened);
        RaisePropertyChanged(nameof(SwitchBottomPanel));

        IsBottomOpened = false;
    }
}

【问题讨论】:

    标签: c# wpf animation binding


    【解决方案1】:

    您可以处理视图模型的PropertyChanged 事件并在视图中以编程方式启动动画:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainViewModel();
            Loaded += OnLoaded;
            Unloaded += OnUnloaded;
        }
    
        private MainViewModel ViewModel => DataContext as MainViewModel;
    
        private void OnLoaded(object sender, RoutedEventArgs e) =>
            ViewModel.PropertyChanged += ViewModel_PropertyChanged;
    
        private void OnUnloaded(object sender, RoutedEventArgs e) =>
            ViewModel.PropertyChanged += ViewModel_PropertyChanged;
    
        private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) =>
            BottomPanel.BeginAnimation(HeightProperty, new DoubleAnimation()
            {
                To = ViewModel.TargetHeight,
                Duration = TimeSpan.FromSeconds(0.3)
            });
    }
    

    仅仅因为您可以在纯 XAML 中实现动画之类的东西,并不意味着您总是应该这样做。这是一个示例,使用像 C# 这样的表达性编程语言而不是像 XAML 这样的标记语言来实现视图逻辑是非常有意义的。

    您可能还需要考虑在视图中指定实际高度,并绑定到IsBottomOpened 属性:

    <Grid x:Name="BottomPanel" Background="Yellow" Height="0" Width="200">
        <Grid.Style>
            <Style TargetType="Grid">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsBottomOpened}">
                        <DataTrigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation Storyboard.TargetProperty="Height"
                                             To="128"
                                             Duration="0:0:0.3" />
                                </Storyboard>
                            </BeginStoryboard>
                        </DataTrigger.EnterActions>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Grid.Style>
    </Grid>
    

    如果您这样做,您还可以将 Button 元素替换为 ToggleButton 并绑定到其 IsChecked 属性,而不是使用 IsBottomOpened 源属性。视图模型中是否真的需要这些属性取决于您的要求。

    【讨论】:

    • DataTrigger 方法效果很好,谢谢!因为我想坚持使用 MVVM,所以我认为最佳实践是 ViewModel 只知道面板的状态,而不是它的大小特征。这种方式与 DataTrigger 完美契合。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-26
    • 1970-01-01
    • 1970-01-01
    • 2013-09-24
    相关资源
    最近更新 更多