Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用绑定到viewmodel的动态宽度和值在WPF(mvvm)中创建倒计时条动画?_C#_Wpf_Xaml_Animation_Mvvm - Fatal编程技术网

C# 如何使用绑定到viewmodel的动态宽度和值在WPF(mvvm)中创建倒计时条动画?

C# 如何使用绑定到viewmodel的动态宽度和值在WPF(mvvm)中创建倒计时条动画?,c#,wpf,xaml,animation,mvvm,C#,Wpf,Xaml,Animation,Mvvm,我想创建一个从当前宽度变为0的条形(矩形)动画,该动画将用作倒计时的可视化。 最后,它应该是这样的: 现在,启动动画的触发器是在静态类中设置的(这部分已经工作了) 我有几点我在这里没有用到: 此视图将位于窗口底部,可缩放。因此,我不知道动画开始时的起始宽度(以像素为单位)。我希望从FilledCountdownBar元素中删除“Width”,让它在开始时自动填充整个空间,但是我无法设置该值的动画(得到一个异常) 当我没有设置动画的“From”属性时,动画不会重置,因为没有开始值,并且在第一

我想创建一个从当前宽度变为0的条形(矩形)动画,该动画将用作倒计时的可视化。 最后,它应该是这样的:

现在,启动动画的触发器是在静态类中设置的(这部分已经工作了)


我有几点我在这里没有用到:

  • 此视图将位于窗口底部,可缩放。因此,我不知道动画开始时的起始宽度(以像素为单位)。我希望从FilledCountdownBar元素中删除“Width”,让它在开始时自动填充整个空间,但是我无法设置该值的动画(得到一个异常)
  • 当我没有设置动画的“From”属性时,动画不会重置,因为没有开始值,并且在第一次播放完动画后,宽度将保持为0
  • 我的viewmodel中有一个属性(持续时间类型),我想将其绑定到动画的持续时间。但看起来我无法在控件模板中执行此操作?将引发异常:
  • 无法冻结此情节提要时间线树以跨线程使用

    我还尝试使用ProgressBar来代替,但我无法让它顺利地设置动画。在更改值时,始终可以看到一些小步骤,因此这对我来说并不是一个真正的选项。

    欢迎提供任何帮助,提前感谢。

    当我需要使用依赖于宽度和类似内容的动态动画时,我总是将其作为附加行为或自定义控件代码在代码中执行

    这允许您在代码中创建
    情节提要
    ,设置其所有动态属性,然后启动它

    在这种情况下,一旦动画开始,它将成为控件开始时的大小。如果用户在窗口运行时调整其大小,动画将不会动态缩放自身。然而,你确实可以做到这一点。我刚刚实现了您的简单的
    双动画

    以下是您案例的工作示例:

    XAML


    非常感谢你!这是非常好的工作,我想我确实理解它是如何工作的。关于“如果用户在窗口运行时调整窗口大小,动画将不会自动缩放。”-部分:我知道这种行为,并且实际上愿意接受这种限制。但是如果你能建议一种更好的方法,我很乐意看一看。:-)
            <Control x:Name="content" Grid.Column="0" Margin="0">
                <Control.Template>
                    <ControlTemplate>
                        <Grid x:Name="FilledCountdownBar" Width="500" HorizontalAlignment="Left" >
                            <Rectangle Fill="#FFA4B5BF"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <DataTrigger Binding="{Binding Path=(Managers:ActionModeManager.ShowUiTimer)}" Value="true">
                                <DataTrigger.EnterActions>
                                    <BeginStoryboard>
                                        <Storyboard>
                                            <DoubleAnimation 
                                Storyboard.TargetName="FilledCountdownBar"
                                Storyboard.TargetProperty="(FrameworkElement.Width)" 
                                To="0" Duration="0:1:0" AutoReverse="False"/>
                                        </Storyboard>
                                    </BeginStoryboard>
                                </DataTrigger.EnterActions>
                            </DataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Control.Template>
            </Control>
    
    <Window x:Class="WpfApp4.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApp4"
        Title="MainWindow"
        Width="800"
        Height="450"
        UseLayoutRounding="True">
        <Window.DataContext>
            <local:MainWindowViewModel />
        </Window.DataContext>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Control x:Name="CountDownVisual"
                Grid.Row="1"
                Height="30"
                Margin="0">
                <Control.Template>
                    <ControlTemplate>
                        <Grid x:Name="RootElement">
                            <Grid x:Name="CountDownBarRootElement"
                                local:CountDownBarAnimationBehavior.IsActive="{Binding ShowUiTimer}"
                                local:CountDownBarAnimationBehavior.ParentElement="{Binding ElementName=RootElement}"
                                local:CountDownBarAnimationBehavior.TargetElement="{Binding ElementName=CountDownBar}">
                                <Rectangle x:Name="CountDownBar"
                                    HorizontalAlignment="Left"
                                    Fill="#FFA4B5BF" />
                            </Grid>
                        </Grid>
                    </ControlTemplate>
                </Control.Template>
            </Control>
        </Grid>
    </Window>
    
    using System;
    using System.Windows;
    using System.Windows.Media.Animation;
    using System.Windows.Threading;
    
    namespace WpfApp4
    {
        public static class CountDownBarAnimationBehavior
        {
            private static Storyboard sb;
    
            #region IsActive (DependencyProperty)
            public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached("IsActive", typeof(bool), typeof(CountDownBarAnimationBehavior), new FrameworkPropertyMetadata(false, OnIsActiveChanged));
    
            public static bool GetIsActive(DependencyObject obj)
            {
                return (bool)obj.GetValue(IsActiveProperty);
            }
    
            public static void SetIsActive(DependencyObject obj, bool value)
            {
                obj.SetValue(IsActiveProperty, value);
            }
    
            private static void OnIsActiveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                if (!(d is FrameworkElement control))
                {
                    return;
                }
    
                if((bool)e.NewValue)
                {
                    if (GetParentElement(control) != null)
                    {
                        StartAnimation(control);
                    }
                    else
                    {
                        // If IsActive is set to true and the other properties haven't
                        // been updated yet, defer the animation until render time.
                        control.Dispatcher?.BeginInvoke((Action) (() => { StartAnimation(control); }), DispatcherPriority.Render);
                    }
                }
                else
                {
                    StopAnimation();
                }
            }
            #endregion
    
            #region ParentElement (DependencyProperty)
            public static readonly DependencyProperty ParentElementProperty = DependencyProperty.RegisterAttached("ParentElement", typeof(FrameworkElement), typeof(CountDownBarAnimationBehavior), new FrameworkPropertyMetadata(null, OnParentElementChanged));
    
            public static FrameworkElement GetParentElement(DependencyObject obj)
            {
                return (FrameworkElement)obj.GetValue(ParentElementProperty);
            }
    
            public static void SetParentElement(DependencyObject obj, FrameworkElement value)
            {
                obj.SetValue(ParentElementProperty, value);
            }
    
            private static void OnParentElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                if(!(d is FrameworkElement fe))
                {
                    return;
                }
    
                // You can wire up events here if you want to react to size changes, etc.
            }
    
            private static void OnParentElementSizeChanged(object sender, SizeChangedEventArgs e)
            {
                if (!(sender is FrameworkElement fe))
                {
                    return;
                }
    
                if (GetIsActive(fe))
                {
                    StopAnimation();
                    StartAnimation(fe);
                }
            }
            #endregion
    
            #region TargetElement (DependencyProperty)
            public static readonly DependencyProperty TargetElementProperty = DependencyProperty.RegisterAttached("TargetElement", typeof(FrameworkElement), typeof(CountDownBarAnimationBehavior), new FrameworkPropertyMetadata(null));
    
            public static FrameworkElement GetTargetElement(DependencyObject obj)
            {
                return (FrameworkElement)obj.GetValue(TargetElementProperty);
            }
    
            public static void SetTargetElement(DependencyObject obj, FrameworkElement value)
            {
                obj.SetValue(TargetElementProperty, value);
            }
            #endregion
    
            private static void StartAnimation(DependencyObject d)
            {
                var parent = GetParentElement(d);
                var target = GetTargetElement(d);
    
                if (parent == null || target == null)
                {
                    return;
                }
    
                sb = new Storyboard();
                var da = new DoubleAnimation();
    
                Storyboard.SetTarget(da, target);
                Storyboard.SetTargetProperty(da, new PropertyPath("Width"));
    
                da.AutoReverse = false;
                da.Duration = new Duration(new TimeSpan(0, 1, 0));
                da.From = parent.ActualWidth;
                da.To = 0d;
    
                sb.Children.Add(da);
    
                sb.Begin();
            }
    
            private static void StopAnimation()
            {
                sb?.Stop();
            }
        }
    }