Wpf 动画控件大小会动态更改

Wpf 动画控件大小会动态更改,wpf,animation,Wpf,Animation,我目前正在开发一种菜单,它有一个“弹出”模式,这意味着它将保持在紧凑模式(只有约60px的宽度),直到你将鼠标移到上面。主题是相当棘手的,因为你不能直接设置控件宽度的动画,设置它会导致一个固定的大小(这不是我想要的) 我的目标是拥有一个控件,该控件可以根据其子控件和其父控件中的最大可用空间具有动态大小 我的理论是:一旦尺寸改变,测量将被称为测量最大可用空间(远远超过将使用的空间)。下一步是安排ArrangeOverride()使用与所需大小相等的参数arrangeBounds调用 现在我缓存这个

我目前正在开发一种菜单,它有一个“弹出”模式,这意味着它将保持在紧凑模式(只有约60px的宽度),直到你将鼠标移到上面。主题是相当棘手的,因为你不能直接设置控件宽度的动画,设置它会导致一个固定的大小(这不是我想要的)

我的目标是拥有一个控件,该控件可以根据其子控件和其父控件中的最大可用空间具有动态大小

我的理论是:一旦尺寸改变,测量将被称为测量最大可用空间(远远超过将使用的空间)。下一步是安排ArrangeOverride()使用与所需大小相等的参数arrangeBounds调用

现在我缓存这个值,如果它与ActualSize属性不同,我将从ActualSize启动一个动画到arrangeBounds.Width。该方法本身确实返回一个大小对象,如
返回base.ArrangeOverride(新大小(WidthWrapper,arrangeBounds.Height))

到目前为止,我有这个(它部分工作-WidthWrapper只是一个DependencyProperty):

受保护的替代尺寸ArrangeOverride(尺寸arrangeBounds)
{
if(数学绝对值(宽度包装)<0.01)
WidthWrapper=arrangeBounds.Width;
if(数学绝对值(arrangeBounds.Width-实际宽度)>0.01)
{
if(_start&(Math.Abs(arrangeBounds.Width-_cache)<0.01))
返回base.ArrangeOverride(新大小(WidthWrapper、arrangeBounds.Height));
if(Math.Abs(arrangeBounds.Width-WidthWrapper)<0.01)
返回base.ArrangeOverride(新大小(WidthWrapper、arrangeBounds.Height));
如果(_启动&!_扩展)
{
控制台。WriteLine(“重新设置动画”);
//更改值
if(_cache
当启动测试项目时,菜单几乎以全尺寸启动,您可以看到它的一部分从右侧“淡入”,这看起来非常酷。现在的问题是,当我切换到压缩模式时,没有动画。它立即变为~61像素宽。当我将鼠标悬停在它上面时,你会看到它在扩展,但会闪烁,它会随机跳回原来的大小

问题很明显,使用这种方法时,控件放错了位置。在制作此动画时,它似乎有点偏离右侧。因此,不再检测到鼠标悬停,它会尝试将动画设置回紧凑的大小。这种情况经常发生,直到动画完成并最终达到完整大小(如果确实如此)

它的父母似乎有一些安置问题。我在这里加了一个GIF,这样你就能明白我的意思了

PS:我禁用了“弹出”模式,但即使是从普通模式切换到紧凑模式也有一些问题(尽管它部分工作并且看起来很不错)

有没有办法解决这个问题,让它顺利扩展而不出现任何闪烁和其他问题?例如,专业用户界面库确实有停靠窗口,这些窗口在取消固定时会很快消失。我想把这种效果用在我的菜单上。我认为这是可能的,因为我已经接近了——但显然WPF不支持它,没有一点技巧

基本上,您需要描述一个打开和关闭的状态,并使用变换为状态之间的运动设置动画。

查看和


基本上,您需要描述一个打开和关闭的状态,并使用变换来设置状态之间运动的动画。

是否有特殊原因,您在后台而不是在XAML中执行此代码?您的动画看起来很简单TranslateTransform@lokusking请参阅我对Clint答案的评论。是否有特殊原因,您在后台而不是在XAML中执行此代码?您的动画看起来很简单TranslateTransform@lokusking请参阅我对Clint答案的评论。但这是否独立于所需的控件大小?我的意思是翻译将是一个固定的值,不是吗?我无法说出控件在紧凑模式或正常模式下的大小。所以这必须是灵活的。好的,
From
to
可以在双动画中绑定。TranslateTransform的X应该是可绑定的too@SharpShade正如lokusking提到的,它只是一个双重动画,与控件的大小相对。好的,这是一个开始。但是我应该绑定到什么呢?我的意思是不能设置宽度的动画,因为这样会禁用自动测量。实际尺寸是w
protected override Size ArrangeOverride(Size arrangeBounds)
{
    if (Math.Abs(WidthWrapper) < 0.01)
        WidthWrapper = arrangeBounds.Width;

    if (Math.Abs(arrangeBounds.Width - ActualWidth) > 0.01)
    {
        if (_started & (Math.Abs(arrangeBounds.Width - _cache) < 0.01))
            return base.ArrangeOverride(new Size(WidthWrapper, arrangeBounds.Height));
        if (Math.Abs(arrangeBounds.Width - WidthWrapper) < 0.01)
            return base.ArrangeOverride(new Size(WidthWrapper, arrangeBounds.Height));

        if (_started & !_isExpanding)
        {
            Console.WriteLine("Re-Animate");
            // Change value
            if (_cache < arrangeBounds.Width)
                _isExpanding = true;
            _cache = arrangeBounds.Width;

            _board.Stop(this);
            _board.Children.Clear();
            _anim = new DoubleAnimation(WidthWrapper, arrangeBounds.Width,
                new Duration(TimeSpan.FromSeconds(0.8)))
            {
                EasingFunction = new QuadraticEase() {EasingMode = EasingMode.EaseInOut}
            };
            _board.Children.Add(_anim);
            Storyboard.SetTarget(_anim, this);
            Storyboard.SetTargetProperty(_anim, new PropertyPath(WidthWrapperProperty));
            _board.Begin(this);

            return base.ArrangeOverride(new Size(WidthWrapper, arrangeBounds.Height));
        }

        if (!_started)
        {
            if (_cache < arrangeBounds.Width)
                _isExpanding = true;
            _cache = arrangeBounds.Width;
            _anim = new DoubleAnimation(WidthWrapper, arrangeBounds.Width,
                new Duration(TimeSpan.FromSeconds(1.5)))
            {
                EasingFunction = new QuadraticEase() {EasingMode = EasingMode.EaseInOut}
            };
            _board.Children.Add(_anim);
            Storyboard.SetTarget(_anim, this);
            Storyboard.SetTargetProperty(_anim, new PropertyPath(WidthWrapperProperty));
            _board.Begin(this, true);
            _started = true;
        }
    }

    Size animatedSize = new Size(WidthWrapper, arrangeBounds.Height);
    return base.ArrangeOverride(animatedSize);
}