C# 用不同颜色的端盖绘制WPF线

C# 用不同颜色的端盖绘制WPF线,c#,wpf,C#,Wpf,WPF的新成员。我需要画一条线(在网格“单元”内,如果有区别的话),它有一个开始/结束的盖子,颜色与线本身不同。似乎在一条线上得到一顶帽子很容易;让它变成不同的颜色并不是那么简单 我已经回顾了直线、多段线、画笔、路径、线几何体和椭圆几何体对象。所有这些似乎都部分解决了我的问题。本文似乎最接近解决方案,但代码示例不完整 很高兴能为我的问题提供任何建议或解决方案。这里有一个想法……使用线性渐变笔刷 <Window.Resources> <LinearGradientBrus

WPF的新成员。我需要画一条线(在网格“单元”内,如果有区别的话),它有一个开始/结束的盖子,颜色与线本身不同。似乎在一条线上得到一顶帽子很容易;让它变成不同的颜色并不是那么简单

我已经回顾了直线、多段线、画笔、路径、线几何体和椭圆几何体对象。所有这些似乎都部分解决了我的问题。本文似乎最接近解决方案,但代码示例不完整


很高兴能为我的问题提供任何建议或解决方案。

这里有一个想法……使用线性渐变笔刷

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


这里有一个想法……使用线性渐变笔刷

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

你可以自己玩。下面是我找到并修改的代码,以满足我的需要。 我添加了一个CapStrokeCapStrokeThickness属性

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)
        {
        }
    }
}
然后使用它:

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

你可以自己玩。下面是我找到并修改的代码,以满足我的需要。 我添加了一个CapStrokeCapStrokeThickness属性

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)
        {
        }
    }
}
然后使用它:

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


WPF形状具有单笔划笔刷。但是,你可以在两条线的上面画两条线,下一条带帽,上一条不带帽。WPF形状有一个单笔划笔刷。你可以在上面画两条线,下一条带帽,上一条不带帽,这正是我想要的。我必须做一些调整,使其符合我的需要,但你为我节省了大量的时间和挫折。谢谢这正是我想要的。我必须做一些调整,使其符合我的需要,但你为我节省了大量的时间和挫折。谢谢这是一个很好的直截了当的建议。渐变在我的情况下不起作用,但它是一种简单的方法,可以在线条的末端添加一些颜色。谢谢。这是一个很好的直截了当的建议。渐变在我的情况下不起作用,但它是一种简单的方法,可以在线条的末端添加一些颜色。谢谢