I am currently implementing a class called SelectionBorder in WPF. It's derived from the Shape class.
我目前正在WPF中实现一个名为SelectionBorder的类。它来自于Shape类。
It basically looks like this:
它基本上是这样的:
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!
}
}
}
The StartPoint and Points properties determine the corners of the border. The border is a typical stroked line border (one black stroke, one invisible stroke like that: - - - -)
StartPoint和Points属性决定边界的各个角落。边界是典型的划线边界(一个黑色的笔划,一个看不见的笔划:- - - - -)
The problem that I have now is that since the corner points are freely choosable it's pretty common that the count of strokes (meaning black and invisible strokes) is not even (in fact not even an integer) and therefore the first stroke looks longer than the others (visible in the picture). This maybe doesn't seem to be a big deal but I later want to animate the border so that the strokes circle round the content. When doing this animation the tiny flaw in the static view becomes clearly visible and in my opinion is highly disturbing.
我现在的问题是,自从choosable*角点是很常见的中风的计数(即黑色和无形的中风)甚至不是(实际上没有一个整数),因此第一次中风看起来比其他人(照片上可见)。这看起来并不是什么大问题,但我后来想把边界动画化,这样笔画就可以围绕内容。在做这个动画时,静态视图中的微小缺陷变得清晰可见,在我看来是非常令人不安的。
alt text http://img14.imageshack.us/img14/2874/selectionborder.png
alt文本http://img14.imageshack.us/img14/2874/selectionborder.png
The problem is that I tried to determine a StrokeLength that gets as close to the original StrokeLength as possible and creates an even number of strokes. However the problem I've run into is that WPF (obviously) can't display the whole precision of a double decimal StrokeLength and therefore the resulting stroke number is uneven once again.
问题是,我试图确定一个StrokeLength尽可能接近原始的StrokeLength并创建偶数个笔划。然而,我遇到的问题是WPF(显然)不能显示双小数StrokeLength的全部精度,因此结果的笔画数再次不均匀。
Is there any workaround for this problem? Do you probably have another solution for my problem?
这个问题有解决办法吗?你对我的问题可能还有别的解决办法吗?
Thanks in advance!
提前谢谢!
EDIT: I retested and reviewed the code after a little break for fitness today and after all it happens only on very big StrokeLengths. I plan to use StrokeLengths of 2 where the little animation jumping does matter much less than I originally thought.
编辑:我重新测试了一下代码,并在今天的适应度稍作休息后重新检查了代码,毕竟它只发生在非常大的StrokeLengths上。我计划使用2的StrokeLengths,在那里小动画跳跃的重要性比我原来认为的要小得多。
2 个解决方案
#1
1
You could make more than one corner "un-matched" in that regard. For example, instead of having one point be the "source" and "destination" of the animated dashes, you could pick 2 points. One would be the "source", dashes appearing to march away from it in 2 directions, and another point be the "destination", where dashes converge and disappear.
在这一点上,你可以使不止一个角落“不匹配”。例如,你可以选择2个点,而不是一个点作为动画破折号的“源”和“目标”。一个是“源”,破折号似乎向两个方向移动,另一个点是“目的地”,即破折号汇聚并消失的地方。
GIMP, for example, animates selection dashed lines in this way and seems to pick a point closest to the lower-left for the "source" and a point closest to the upper-right for the "destination".
例如,GIMP以这种方式对选择虚线进行动画处理,并似乎为“源”选择离左下角最近的点,为“目标”选择离右上角最近的点。
You could come up with some other scheme, as well.
你也可以想出别的办法。
Just remember that while it may look disturbing to you, most users will not care.
只要记住,虽然它可能会让你感到不安,但大多数用户不会在意。
#2
0
I just found a way that makes it way easier to create such an animated SelectionBorder.
我刚刚找到了一种方法,可以更容易地创建这样一个动画选择边界。
Instead of creating the animation by moving an self-created AnimationPoint through animation I just animated the StrokeDashOffset property natively provided by the Shape class and setting the StrokeDashArray to define the StrokeLength.
我没有通过在动画中移动一个自创建的动画点来创建动画,而是对Shape类本身提供的StrokeDashOffset属性进行动画处理,并设置StrokeDashArray来定义StrokeLength。
It would look like this in XAML:
XAML是这样的
<namespace:SelectionBorder StrokeDashArray="2" AnimationDuration="0:0:1" Stroke="Black" />
The class looks like this:
这个类是这样的:
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;
}
}
}
#1
1
You could make more than one corner "un-matched" in that regard. For example, instead of having one point be the "source" and "destination" of the animated dashes, you could pick 2 points. One would be the "source", dashes appearing to march away from it in 2 directions, and another point be the "destination", where dashes converge and disappear.
在这一点上,你可以使不止一个角落“不匹配”。例如,你可以选择2个点,而不是一个点作为动画破折号的“源”和“目标”。一个是“源”,破折号似乎向两个方向移动,另一个点是“目的地”,即破折号汇聚并消失的地方。
GIMP, for example, animates selection dashed lines in this way and seems to pick a point closest to the lower-left for the "source" and a point closest to the upper-right for the "destination".
例如,GIMP以这种方式对选择虚线进行动画处理,并似乎为“源”选择离左下角最近的点,为“目标”选择离右上角最近的点。
You could come up with some other scheme, as well.
你也可以想出别的办法。
Just remember that while it may look disturbing to you, most users will not care.
只要记住,虽然它可能会让你感到不安,但大多数用户不会在意。
#2
0
I just found a way that makes it way easier to create such an animated SelectionBorder.
我刚刚找到了一种方法,可以更容易地创建这样一个动画选择边界。
Instead of creating the animation by moving an self-created AnimationPoint through animation I just animated the StrokeDashOffset property natively provided by the Shape class and setting the StrokeDashArray to define the StrokeLength.
我没有通过在动画中移动一个自创建的动画点来创建动画,而是对Shape类本身提供的StrokeDashOffset属性进行动画处理,并设置StrokeDashArray来定义StrokeLength。
It would look like this in XAML:
XAML是这样的
<namespace:SelectionBorder StrokeDashArray="2" AnimationDuration="0:0:1" Stroke="Black" />
The class looks like this:
这个类是这样的:
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;
}
}
}