Создание SelectionBorder:Укусить в лицо десятичным округлением?
Вопрос
В настоящее время я реализую класс SelectionBorder в WPF.Он является производным от класса 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.Я планирую использовать StrokeLengths, равный 2, где небольшие прыжки анимации имеют гораздо меньшее значение, чем я изначально думал.
Решение 2
Я только что нашел способ, который упрощает создание такого анимированного SelectionBorder.
Вместо создания анимации путем перемещения самостоятельно созданного AnimationPoint через анимацию я просто анимировал свойство StrokeDashOffset, изначально предоставляемое классом Shape, и установил 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;
}
}
}
Другие советы
В этом отношении вы можете сделать несколько углов «непревзойденными».Например, вместо того, чтобы одна точка была «источником» и «назначением» анимированных штрихов, вы можете выбрать две точки.Одна из них будет «источником», от которого кажется, что черточки расходятся в двух направлениях, а другая — «пунктом назначения», где черточки сходятся и исчезают.
GIMP, например, анимирует пунктирные линии выбора таким образом и, кажется, выбирает точку, ближайшую к левому нижнему углу для «источника», и точку, ближайшую к верхнему правому углу, для «назначения».
Можно придумать и другую схему.
Просто помните, что, хотя это может показаться вам тревожным, большинству пользователей это все равно.