简体   繁体   中英

Draw a WPF Line With Different Colored End Caps

New to WPF. I need to draw a line (inside a grid "cell", if that makes a difference) which has a start/end cap that is a different color than the line itself. Seems like getting a cap on a line is easy; getting it to be a different color is not so simple.

I've reviewed the Line, PolyLine, Pen, Path, LineGeometry and EllipseGeometry objects. All of these seem to offer partial solution to my problem. This article seems to come the closest to a solution, but the code example is incomplete.

Happy to entertain any suggestions or solutions to my problem.

Here is one idea...use a linear gradient brush

<Window.Resources>
    <LinearGradientBrush x:Key="gb" StartPoint="0 0" EndPoint="1 0">
        <LinearGradientBrush.GradientStops>
            <GradientStop Offset="0.0" Color="Red"/>
            <GradientStop Offset="0.1" Color="Black"/>
            <GradientStop Offset="0.9" Color="Black"/>
            <GradientStop Offset="1.0" Color="Red"/>
        </LinearGradientBrush.GradientStops>
    </LinearGradientBrush>
</Window.Resources>
<Grid>
    <Line X1="100" X2="200" Y1="100" Y2="100" Stroke="{StaticResource gb}" StrokeThickness="5"/>
</Grid>

You could roll your own. Here is code that I found and modified to suit my needs. I added a CapStroke and CapStrokeThickness property

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;

namespace LineCaps
{

    public class CappedLine: FrameworkElement
    {        
        protected override void  OnRender(DrawingContext dc)
        {
            Point pos, tangent;
            double angleInRadians;
            double angleInDegrees;
            TransformGroup tg;
            Pen pen = new Pen(Stroke, StrokeThickness);
            dc.DrawGeometry(null, pen, LinePath);

            Pen capPen = new Pen(CapStroke, CapStrokeThickness);
            if (BeginCap != null)
            {
                LinePath.GetPointAtFractionLength(0.01d, out pos, out tangent);
                angleInRadians = Math.Atan2(tangent.Y, tangent.X) + Math.PI;
                angleInDegrees = angleInRadians * 180 / Math.PI + 180;
                tg = new TransformGroup();
                tg.Children.Add(new RotateTransform(angleInDegrees));
                LinePath.GetPointAtFractionLength(0.0d, out pos, out tangent);
                tg.Children.Add(new TranslateTransform(pos.X, pos.Y));
                dc.PushTransform(tg);
                dc.DrawGeometry(CapStroke, capPen, BeginCap);
                dc.Pop();
            }
            if (EndCap != null)
            {
                LinePath.GetPointAtFractionLength(0.99, out pos, out tangent);
                angleInRadians = Math.Atan2(tangent.Y, tangent.X) + Math.PI;
                angleInDegrees = angleInRadians * 180 / Math.PI;
                tg = new TransformGroup();
                tg.Children.Add(new RotateTransform(angleInDegrees));
                LinePath.GetPointAtFractionLength(1, out pos, out tangent);
                tg.Children.Add(new TranslateTransform(pos.X, pos.Y));
                dc.PushTransform(tg);
                dc.DrawGeometry(CapStroke, capPen, EndCap);
            }         
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            //TODO: Consider creating the Pen once when Stroke and StrokeThickness are set
            return LinePath.GetRenderBounds(new Pen(Stroke, StrokeThickness)).Size;
        }

        public static readonly DependencyProperty StrokeProperty = Shape.StrokeProperty.AddOwner(typeof(CappedLine));
        public Brush Stroke
        {
            get { return (Brush)GetValue(StrokeProperty); }
            set { SetValue(StrokeProperty, value); }
        }

        public static readonly DependencyProperty StrokeThicknessProperty = Shape.StrokeThicknessProperty.AddOwner(typeof(CappedLine));
        public double StrokeThickness
        {
            get { return (double)GetValue(StrokeThicknessProperty); }
            set { SetValue(StrokeThicknessProperty, value); }
        }

        public static DependencyProperty CapStrokeProperty =
        DependencyProperty.Register(
            "CapStroke",
            typeof(Brush),
            typeof(CappedLine));
        public Brush CapStroke
        {
            get { return (Brush)GetValue(CapStrokeProperty); }
            set { SetValue(CapStrokeProperty, value); }
        }

        public static readonly DependencyProperty CapStrokeThicknessProperty =
        DependencyProperty.Register(
            "CapStrokeThickness",
            typeof(double),
            typeof(CappedLine));

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

        public static readonly DependencyProperty LinePathProperty =
            DependencyProperty.Register("LinePath", typeof(PathGeometry), typeof(CappedLine),
            new FrameworkPropertyMetadata(
                null,
                FrameworkPropertyMetadataOptions.AffectsRender, // choose appropriate flags here!!!
                LinePathChangedCallback));
        static void LinePathChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            CappedLine me = sender as CappedLine;
            if (null != me)
            {
                me.OnLinePathChanged((PathGeometry)args.NewValue);
            }
        }
        public PathGeometry LinePath
        {
            get { return (PathGeometry)GetValue(LinePathProperty); }
            set { SetValue(LinePathProperty, value); }
        }
        public virtual void OnLinePathChanged(PathGeometry value)
        {
        }

        public static readonly DependencyProperty BeginCapProperty =
            DependencyProperty.Register("BeginCap", typeof(Geometry), typeof(CappedLine),
            new FrameworkPropertyMetadata(
                null,
                FrameworkPropertyMetadataOptions.AffectsRender, // choose appropriate flags here!!!
                BeginCapChangedCallback));
        static void BeginCapChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            CappedLine me = sender as CappedLine;
            if (null != me)
            {
                me.OnBeginCapChanged((Geometry)args.NewValue);
            }
        }
        public Geometry BeginCap
        {
            get { return (Geometry)GetValue(BeginCapProperty); }
            set { SetValue(BeginCapProperty, value); }
        }
        public virtual void OnBeginCapChanged(Geometry value)
        {
        }

        public static readonly DependencyProperty EndCapProperty =
            DependencyProperty.Register("EndCap", typeof(Geometry), typeof(CappedLine),
            new FrameworkPropertyMetadata(
                null,
                FrameworkPropertyMetadataOptions.AffectsRender, // choose appropriate flags here!!!
                EndCapChangedCallback));
        static void EndCapChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            CappedLine me = sender as CappedLine;
            if (null != me)
            {
                me.OnEndCapChanged((Geometry)args.NewValue);
            }
        }
        public Geometry EndCap
        {
            get { return (Geometry)GetValue(EndCapProperty); }
            set { SetValue(EndCapProperty, value); }
        }
        public virtual void OnEndCapChanged(Geometry value)
        {
        }
    }
}

Then to use it:

<Window x:Class="LineCaps.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:loc="clr-namespace:LineCaps"
        Title="MainWindow">
    <Viewbox>
        <Canvas Height="350" Width="525">
            <loc:CappedLine BeginCap="M0,0 L6,-6 L6,6 z" EndCap="M0,-3 L0,3 L6,3 L6,-3 z" Stroke="Gold" StrokeThickness="1" CapStroke="Green" CapStrokeThickness="1" Canvas.Left="40" Canvas.Top="60">
                <loc:CappedLine.LinePath>
                    <PathGeometry Figures="M0,0 L120,120" />
                </loc:CappedLine.LinePath>
            </loc:CappedLine>

            <loc:CappedLine EndCap="M0,0 L6,-6 L6,6 z" Stroke="Blue" StrokeThickness="1" CapStroke="Red" CapStrokeThickness="1"  Canvas.Left="40" Canvas.Top="200" RenderTransformOrigin="1.5,0.5">
                <loc:CappedLine.BeginCap>
                    <EllipseGeometry Center="0,0" RadiusX="6" RadiusY="6" />
                </loc:CappedLine.BeginCap>
                <loc:CappedLine.LinePath>
                    <PathGeometry Figures="M0,0 C1,1 10.5,75.5 48.5,66.5 86.5,57.5 53.5,3.5000146 105.5,16.500091 157.5,29.500166 164.5,87.500505 164.5,87.500505" />
                </loc:CappedLine.LinePath>
                <loc:CappedLine.RenderTransform>
                    <RotateTransform Angle="0" />
                </loc:CappedLine.RenderTransform>
            </loc:CappedLine>

        </Canvas>
    </Viewbox>
</Window>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM