2011-10-02 27 views
0

我需要画两条相邻的颜色线。 我发现的唯一解决方案是基于两条线,第二条是TranslateTransform。 但是,翻译值必须根据行方向(角度)进行更改。Silverlight双色线

有没有办法更容易地做到这一点?

感谢您的帮助。

回答

2

可以使用具有四个GradientStop的LinearGradientBrush绘制双色线。例如,下面的XAML画一条水平线是半红半黄:

<Line X1="0" Y1="20" X2="200" Y2="20" StrokeThickness="14"> 
     <Line.Stroke> 
      <LinearGradientBrush StartPoint="0,0" EndPoint="0,1"> 
       <GradientStop Offset="0" Color="Red" /> 
       <GradientStop Offset="0.5" Color="Red" /> 
       <GradientStop Offset="0.5" Color="Yellow" /> 
       <GradientStop Offset="1" Color="Yellow" /> 
      </LinearGradientBrush> 
     </Line.Stroke> 
    </Line> 

如果您尝试使用带线不是水平同一个LinearGradientBrush,你很快就会发现,它不会做你想做的事。这两种颜色总是由一条水平线分隔,无论该线具有何种渐变,而您希望将这两种颜色分开的线沿着您的Line的中间线运行。要实现此目的,您需要更改LinearGradientBrush的StartPointEndPoint。这取决于线的梯度,并且它们有点难以计算。

为了演示如何做到这一点,我组装了一个模板化控件(下图),绘制双色线。如果你站在(X1Y1)并朝向(X2,Y2)和Color2将在您的右侧,则会在左侧的颜色为Color1

请注意,此控件假定行的开始和结束标记为Flat。如果您希望使用其他类型的开始或结束上限(例如SquareRound),则需要调整overallWidthoverallHeight的计算。

TwoColorLine.cs:

public class TwoColorLine : Control 
{ 
    public static readonly DependencyProperty X1Property = 
     DependencyProperty.Register("X1", typeof(double), typeof(TwoColorLine), new PropertyMetadata(Coordinates_Changed)); 

    public static readonly DependencyProperty Y1Property = 
     DependencyProperty.Register("Y1", typeof(double), typeof(TwoColorLine), new PropertyMetadata(Coordinates_Changed)); 

    public static readonly DependencyProperty X2Property = 
     DependencyProperty.Register("X2", typeof(double), typeof(TwoColorLine), new PropertyMetadata(Coordinates_Changed)); 

    public static readonly DependencyProperty Y2Property = 
     DependencyProperty.Register("Y2", typeof(double), typeof(TwoColorLine), new PropertyMetadata(Coordinates_Changed)); 

    public static readonly DependencyProperty Color1Property = 
     DependencyProperty.Register("Color1", typeof(Color), typeof(TwoColorLine), new PropertyMetadata(Colors_Changed)); 

    public static readonly DependencyProperty Color2Property = 
     DependencyProperty.Register("Color2", typeof(Color), typeof(TwoColorLine), new PropertyMetadata(Colors_Changed)); 

    public static readonly DependencyProperty StrokeThicknessProperty = 
     DependencyProperty.Register("StrokeThickness", typeof(double), typeof(TwoColorLine), null); 

    private LinearGradientBrush lineBrush; 

    public TwoColorLine() 
    { 
     this.DefaultStyleKey = typeof(TwoColorLine); 
    } 

    public double X1 
    { 
     get { return (double)GetValue(X1Property); } 
     set { SetValue(X1Property, value); } 
    } 

    public double Y1 
    { 
     get { return (double)GetValue(Y1Property); } 
     set { SetValue(Y1Property, value); } 
    } 

    public double X2 
    { 
     get { return (double)GetValue(X2Property); } 
     set { SetValue(X2Property, value); } 
    } 

    public double Y2 
    { 
     get { return (double)GetValue(Y2Property); } 
     set { SetValue(Y2Property, value); } 
    } 

    public Color Color1 
    { 
     get { return (Color)GetValue(Color1Property); } 
     set { SetValue(Color1Property, value); } 
    } 

    public Color Color2 
    { 
     get { return (Color)GetValue(Color2Property); } 
     set { SetValue(Color2Property, value); } 
    } 

    public double StrokeThickness 
    { 
     get { return (double)GetValue(StrokeThicknessProperty); } 
     set { SetValue(StrokeThicknessProperty, value); } 
    } 

    private static void Coordinates_Changed(DependencyObject obj, DependencyPropertyChangedEventArgs e) 
    { 
     var line = obj as TwoColorLine; 
     if (line != null) 
     { 
      line.OnCoordinatesChanged(); 
     } 
    } 

    private static void Colors_Changed(DependencyObject obj, DependencyPropertyChangedEventArgs e) 
    { 
     var line = obj as TwoColorLine; 
     if (line != null) 
     { 
      line.OnColorsChanged(); 
     } 
    } 

    private void OnCoordinatesChanged() 
    { 
     if (lineBrush != null) 
     { 
      RecalculateEndPoints(); 
     } 
    } 

    public void OnColorsChanged() 
    { 
     if (lineBrush != null) 
     { 
      UpdateColors(); 
     } 
    } 

    public void UpdateColors() 
    { 
     lineBrush.GradientStops[0].Color = Color1; 
     lineBrush.GradientStops[1].Color = Color1; 
     lineBrush.GradientStops[2].Color = Color2; 
     lineBrush.GradientStops[3].Color = Color2; 
    } 

    public override void OnApplyTemplate() 
    { 
     base.OnApplyTemplate(); 
     Line line = GetTemplateChild("line") as Line; 
     if (line == null) 
     { 
      throw new InvalidOperationException("No line found in the template"); 
     } 

     lineBrush = line.Stroke as LinearGradientBrush; 
     if (lineBrush == null) 
     { 
      throw new InvalidOperationException("Line does not have a LinearGradientBrush as its stroke"); 
     } 

     UpdateColors(); 
     RecalculateEndPoints(); 
    } 

    private void RecalculateEndPoints() 
    { 
     double cos, sin; 
     if (X2 == X1) 
     { 
      cos = 0; 
      sin = (Y2 > Y1) ? 1 : -1; 
     } 
     else 
     { 
      double gradient = (Y2 - Y1)/(X2 - X1); 
      cos = Math.Sqrt(1/(1 + gradient * gradient)); 
      sin = gradient * cos; 
     } 

     // These two lines assume flat start and end cap. 
     double overallWidth = Math.Abs(X2 - X1) + StrokeThickness * Math.Abs(sin); 
     double overallHeight = Math.Abs(Y2 - Y1) + StrokeThickness * Math.Abs(cos); 

     int sign = (X2 < X1) ? -1 : 1; 

     double xOffset = (sign * StrokeThickness * sin/2)/overallWidth; 
     double yOffset = (sign * StrokeThickness * cos/2)/overallHeight; 

     lineBrush.StartPoint = new Point(0.5 + xOffset, 0.5 - yOffset); 
     lineBrush.EndPoint = new Point(0.5 - xOffset, 0.5 + yOffset); 
    } 
} 

主题\ Generic.xaml:

<Style TargetType="local:TwoColorLine"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="local:TwoColorLine"> 
       <Line x:Name="line" X1="{TemplateBinding X1}" Y1="{TemplateBinding Y1}" X2="{TemplateBinding X2}" Y2="{TemplateBinding Y2}" StrokeThickness="{TemplateBinding StrokeThickness}"> 
        <Line.Stroke> 
         <LinearGradientBrush> 
          <GradientStop Offset="0" /> 
          <GradientStop Offset="0.5" /> 
          <GradientStop Offset="0.5" /> 
          <GradientStop Offset="1" /> 
         </LinearGradientBrush> 
        </Line.Stroke> 
       </Line> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

实例:

<Grid> 
    <local:TwoColorLine X1="190" Y1="170" X2="150" Y2="50" Color1="Red" Color2="Green" StrokeThickness="14" /> 
    <local:TwoColorLine X1="210" Y1="170" X2="250" Y2="50" Color1="Red" Color2="Green" StrokeThickness="14" /> 
    <local:TwoColorLine X1="230" Y1="190" X2="350" Y2="150" Color1="Red" Color2="Green" StrokeThickness="14" /> 
    <local:TwoColorLine X1="230" Y1="210" X2="350" Y2="250" Color1="Red" Color2="Green" StrokeThickness="14" /> 
    <local:TwoColorLine X1="210" Y1="230" X2="250" Y2="350" Color1="Red" Color2="Green" StrokeThickness="14" /> 
    <local:TwoColorLine X1="190" Y1="230" X2="150" Y2="350" Color1="Red" Color2="Green" StrokeThickness="14" /> 
    <local:TwoColorLine X1="170" Y1="210" X2="50" Y2="250" Color1="Red" Color2="Green" StrokeThickness="14" /> 
    <local:TwoColorLine X1="170" Y1="190" X2="50" Y2="150" Color1="Red" Color2="Green" StrokeThickness="14" /> 
</Grid> 

EDIT:修改RecalculateEndPoints()方法把StartPoint和终点在线的边缘而不是临界点n在线的边界矩形上。修改后的方法非常简单,使用两种以上颜色的控件更容易。

+0

伟大的解决方案!非常感谢! –

+0

现在我试图在两行之间添加透明分隔符。我想我必须添加一个透明的渐变停止。此外,我需要使两条线变得可变,取决于限制值。任何建议? –

+0

我想我需要一个三种颜色的线与中间透明的颜色。我试图去理解它,但是我无法管理线条上的宽度。 –

0

您可以用颜色创建一个小的ImageBrush,并使用它绘制一条线。

+0

Silverlight不支持平铺图像画笔。恐怕这个解决方案不适用。 –

+0

呵呵 - 看起来像其他人也遇到过这种情况。使用着色器似乎是其他人采用的解决方法,例如http://silverscratch.blogspot.com/2010/09/tiled-image-brush-for-silverlight.html – holtavolt