WPF ArcSegment具有3种颜色渐变

时间:2018-10-24 12:58:29

标签: c# wpf

我尝试在WPF控件中绘制一个ArcSegment,该控件用作循环进度条并具有三个颜色渐变。它应以0%的红色开始,以50%的黄色开始,以100%的绿色开始。因此,对于75%的用户,其外观应为:

Like is should look like

我尝试使用LinearGradientBrush

<Path StrokeThickness="25">
    <Path.Data>
        <PathGeometry>
            <PathFigure StartPoint="50, 12.5">

                <ArcSegment RotationAngle="0" SweepDirection="Clockwise"
                            IsLargeArc="true"
                            Point="12.5, 75" 
                            Size="62.5, 62.5">
                </ArcSegment>
            </PathFigure>
        </PathGeometry>
    </Path.Data>
    <Path.Stroke>
        <LinearGradientBrush StartPoint="0, 0.5" EndPoint="1, 0.5">
            <GradientStop Offset="0" Color="Green" />
            <GradientStop Offset="0.5" Color="Red" />
            <GradientStop Offset="1.0" Color="Yellow"/>
        </LinearGradientBrush>
    </Path.Stroke>
</Path>

或在其他方向:

<Path.Stroke>
    <LinearGradientBrush StartPoint="0.5, 0" EndPoint="0.5, 1">
        <GradientStop Offset="0" Color="Green" />
        <GradientStop Offset="0.5" Color="Red" />
        <GradientStop Offset="1.0" Color="Yellow"/>
    </LinearGradientBrush>
</Path.Stroke>

但是问题是渐变用于整个表面,所以我用红色涂了两次而不是一次,并且在循环开始时不正确:

Look with Solid gradient

我尝试将图片刷与需要渐变的图片一起使用。

<Path.Stroke>
    <ImageBrush ImageSource="../brush.png"  />
</Path.Stroke>

对于大于75%的值,此方法效果很好,但对于较小的值,还会绘制绿色部分,这是不应该的。

enter image description here

有什么想法的人可以做些什么?

3 个答案:

答案 0 :(得分:0)

我认为使用线性渐变笔刷无法实现这种显示。

我的解决方案是将控件分成多个小的楔形元素,每个元素都有一个单色。每个元素的颜色是根据其角度和控件的开始/结束角度(如果要努力创建这样的控件,则最好使其更通用)来计算的,并且需要颜色停止。

不过,您不能只使用红色/绿色/蓝色颜色空间并为单个元素插值颜色-您必须使用Hue / Saturation / Luminosity之类的东西。

答案 1 :(得分:0)

我通过ImageBrush使用它。这样做的目的是仅使用需要裁剪的图像部分。

为此,我使用了一个转换器,该转换器返回ImageBrush的当前百分比值。

public class ValueToImageBrush : IValueConverter
{
    private const string _filePath = "pack://application:,,,/XXX;component/brush.png";
    private BitmapImage _baseImage;

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var doubleValue = value as double?;
        if (doubleValue == null) return new SolidColorBrush(Colors.LightSlateGray);

        var baseImg = LoadImage();           
        var img = CutImage(baseImg, doubleValue.Value);

        return InitImageBrush(img);
    }       


    private BitmapImage LoadImage()
    {
        if (_baseImage == null)
        {
            _baseImage = new BitmapImage();
            _baseImage.BeginInit();
            _baseImage.UriSource = new Uri(_filePath);
            _baseImage.EndInit();
        }
        return _baseImage;
    }

    private static ImageSource CutImage(BitmapImage baseImg, double doubleValue)
    {
        var img = baseImg as ImageSource;
        if (doubleValue < 50)
        {
            var usedHight = Math.Max(25.0, baseImg.PixelHeight * (doubleValue * 2 / 100));
            img = new CroppedBitmap(baseImg,
                new Int32Rect(baseImg.PixelWidth / 2, 0, baseImg.PixelWidth / 2, (int)usedHight));
        }
        else if (doubleValue < 75)
        {
            var usedWidth = baseImg.PixelWidth * (doubleValue / 100);
            img = new CroppedBitmap(baseImg, new Int32Rect(200 - (int)usedWidth, 0, (int)usedWidth, baseImg.PixelHeight));
        }

        return img;
    }
    private static object InitImageBrush(ImageSource img)
    {
        ImageBrush imgBrush = new ImageBrush();
        imgBrush.ImageSource = img;
        imgBrush.Stretch = Stretch.None;
        return imgBrush;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

我不确定这是否是最聪明的解决方案,但它执行速度很快,实现起来并不复杂。可能是其他人有更好的解决方案。

答案 2 :(得分:0)

我意识到,这有点棘手,但是如果起点在顶部,底部,左侧或右侧,那么将ArcSegment最多分为四个部分会有所帮助。 我明天可以解释更多,但是棘手的部分是找到从每个ArcSegment的起点到终点的对角线上的梯度步长。 如果起点在任意位置,则投影线可能会具有非直角,因此很难找到。