2011-10-02 55 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