2

我目前正在 WPF 中实现一个名为 SelectionBorder 的类。它派生自 Shape 类。

它基本上看起来像这样:

public class SelectionBorder : Shape
{
   public Point StartPoint {get; set;}
   public PointCollection Points {get; set;}

   public double StrokeLength {get; set;}

   protected override Geometry DefiningGeometry{
       get{
           //Magic!
       }
   }

}

StartPoint 和 Points 属性确定边框的角。边框是典型的描边线边框(一个黑色描边,一个隐形描边像这样:- - - -)

我现在遇到的问题是,由于角点是可以自由选择的,因此笔画数(即黑色和不可见笔画)不是偶数(实际上甚至不是整数)是很常见的,因此第一个笔画看起来比其他(在图片中可见)。这似乎没什么大不了的,但我后来想为边框设置动画,以便笔画围绕内容。在制作这个动画时,静态视图中的微小缺陷变得清晰可见,在我看来非常令人不安。

替代文字 http://img14.imageshack.us/img14/2874/selectionborder.png

问题是我试图确定一个尽可能接近原始 StrokeLength 并创建偶数笔划的 StrokeLength。但是我遇到的问题是 WPF(显然)无法显示双十进制 StrokeLength 的全部精度,因此生成的笔画数再次不均匀。

这个问题有什么解决方法吗?您可能对我的问题有其他解决方案吗?

提前致谢!

编辑:今天我为了健身稍作休息后重新测试和审查了代码,毕竟它只发生在非常大的 StrokeLengths 上。我计划使用 2 的 StrokeLengths,其中小动画跳跃的重要性比我最初想象的要小得多。

4

2 回答 2

1

在这方面,您可以使多个角落“不匹配”。例如,您可以选择 2 个点,而不是让一个点作为动画破折号的“源”和“目的地”。一个是“源”,破折号似乎从两个方向远离它,另一点是“目的地”,破折号会聚并消失。

例如,GIMP 以这种方式为选择虚线设置动画,并且似乎选择了一个最靠近左下角的点作为“源”,而选择一个最靠近右上角的点作为“目标”。

你也可以想出一些其他的方案。

请记住,虽然它可能会让您感到不安,但大多数用户不会在意。

于 2009-08-19T17:15:37.810 回答
0

我刚刚找到了一种方法,可以更轻松地创建这样一个动画 SelectionBorder。

我没有通过动画移动自己创建的 AnimationPoint 来创建动画,而是为 Shape 类本机提供的 StrokeDashOffset 属性设置动画,并设置 StrokeDashArray 来定义 StrokeLength。

在 XAML 中看起来像这样:

<namespace:SelectionBorder StrokeDashArray="2" AnimationDuration="0:0:1" Stroke="Black" />

该类如下所示:

public class SelectionBorder : Shape
{
    private DoubleAnimation m_Animation;
    private bool m_AnimationStarted;

    public SelectionBorder()
    {
        IsVisibleChanged += OnIsVisibleChanged;
    }

    protected void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        if (Visibility == Visibility.Visible)
        {
            StartAnimation();
        }
        else
        {
            StopAnimation();
        }
    }

    public void StartAnimation()
    {
        if (m_AnimationStarted)
            return;

        if (m_Animation == null)
        {
            m_Animation = CreateAnimation();
        }

        BeginAnimation(StrokeDashOffsetProperty, m_Animation);
        m_AnimationStarted = true;
    }

    protected virtual DoubleAnimation CreateAnimation()
    {
        DoubleAnimation animation = new DoubleAnimation();
        animation.From = 0;
        if (StrokeDashArray.Count == 0)
            animation.To = 4;
        else
            animation.To = StrokeDashArray.First() * 2;
        animation.Duration = AnimationDuration;
        animation.RepeatBehavior = RepeatBehavior.Forever;
        return animation;
    }

    public void StopAnimation()
    {
        if (m_AnimationStarted)
        {
            BeginAnimation(StrokeDashOffsetProperty, null);
            m_AnimationStarted = false;
        }
    }

    #region Dependency Properties

    public Duration AnimationDuration
    {
        get { return (Duration)GetValue(AnimationDurationProperty); }
        set { SetValue(AnimationDurationProperty, value); }
    }

    public static readonly DependencyProperty AnimationDurationProperty =
        DependencyProperty.Register("AnimationDuration", typeof(Duration), typeof(SelectionBorder), new UIPropertyMetadata(new Duration(TimeSpan.FromSeconds(0.5))));


    #endregion Dependency Properties

    protected override Geometry DefiningGeometry
    {
        get
        {
            double width = (double.IsNaN(Width)) ? ((Panel)Parent).ActualWidth : Width;
            double height = (double.IsNaN(Height)) ? ((Panel)Parent).ActualHeight : Height;

            RectangleGeometry geometry = new RectangleGeometry(new Rect(0, 0, width, height));

            return geometry;
        }
    }
}
于 2009-08-24T10:55:22.787 回答