开发者

Silverlight double color lines

I need to draw a line 开发者_开发百科with two adjacent color. The only solution i found is based on two lines, with a TranslateTransform on the second. But the translate value have to change according to line orientation (angle).

Is the any way to do it more easily?

Thanks for help.


You can draw a two-colour line using a LinearGradientBrush with four GradientStops. For example, the following XAML draws a horizontal line that is half red and half yellow:

    <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>

If you attempt to use the same LinearGradientBrush with a line that is not horizontal, you will quickly find that it won't do what you want. The two colours are always separated by a horizontal line whatever gradient the line has, whereas you want the line that separates the two colours to run down the middle of your Line. To achieve this, you need to change the StartPoint and EndPoint for the LinearGradientBrush. These depend on the gradient of the line, and they are a little awkward to calculate.

To demonstrate how this can be done, I've put together a templated control (below) that draws two-colour lines. Color1 is the colour that would be on your left if you stood at (X1, Y1) and looked towards (X2, Y2), and Color2 would be on your right.

Note that this control assumes that the start and end caps of the line are Flat. If you wish to use other types of start or end cap (e.g. Square or Round), you will need to adjust the calculation of overallWidth and overallHeight.

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);
    }
}

Themes\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>

Example usage:

<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: revise RecalculateEndPoints() method to put the StartPoint and EndPoint on the edge of the line rather than on the line's bounding rectangle. The revised method is much simpler and makes it easier to use the control with more than two colours.


You can create a small ImageBrush with the colors, and draw a single line using it.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜