wpf 网格背景

原来使用控件,但是鼠标事件会受到子控件影响,现在改成装饰器

xaml


    <ctrlHelpers:GridLineDecorator>
        <Canvas />
    </ctrlHelpers:GridLineDecorator>

cs

public class GridLineDecorator : Decorator
{
    public static readonly DependencyProperty ForegroundProperty = TextElement.ForegroundProperty.AddOwner(
        typeof(GridLineDecorator));

    public static readonly DependencyProperty FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(
        typeof(GridLineDecorator));

    public static readonly DependencyProperty FontStyleProperty = TextElement.FontStyleProperty.AddOwner(
        typeof(GridLineDecorator));

    public static readonly DependencyProperty FontWeightProperty = TextElement.FontWeightProperty.AddOwner(
        typeof(GridLineDecorator));

    public static readonly DependencyProperty FontStretchProperty = TextElement.FontStretchProperty.AddOwner(
        typeof(GridLineDecorator));

    public static readonly DependencyProperty FontSizeProperty = TextElement.FontSizeProperty.AddOwner(
        typeof(GridLineDecorator));

    private readonly double pixelsPerDip;

    private readonly IDictionary<FontWeight, Pen> penDict = new Dictionary<FontWeight, Pen>();
    private readonly Pen defaultPen;

    private Typeface? defaultTypeface;

    public GridLineDecorator()
    {
        this.Opacity = 0.75;
        this.Loaded += this.OnLoaded;
        this.Foreground = Brushes.CadetBlue;
        this.defaultPen = new Pen(this.Foreground, 1);
        this.pixelsPerDip = DpiHelper.GetScale(this);
    }

    public Brush Foreground
    {
        get => (Brush)this.GetValue(ForegroundProperty);
        set => this.SetValue(ForegroundProperty, value);
    }

    public FontFamily FontFamily
    {
        get => (FontFamily)this.GetValue(FontFamilyProperty);
        set => this.SetValue(FontFamilyProperty, value);
    }

    public FontStyle FontStyle
    {
        get => (FontStyle)this.GetValue(FontStyleProperty);
        set => this.SetValue(FontStyleProperty, value);
    }

    public FontWeight FontWeight
    {
        get => (FontWeight)this.GetValue(FontWeightProperty);
        set => this.SetValue(FontWeightProperty, value);
    }

    public FontStretch FontStretch
    {
        get => (FontStretch)this.GetValue(FontStretchProperty);
        set => this.SetValue(FontStretchProperty, value);
    }

    public double FontSize
    {
        get => (double)this.GetValue(FontSizeProperty);
        set => this.SetValue(FontSizeProperty, value);
    }

    protected override void OnMouseEnter(MouseEventArgs e)
    {
        base.OnMouseEnter(e);

        this.InvalidateVisual();
    }

    protected override void OnMouseLeave(MouseEventArgs e)
    {
        base.OnMouseLeave(e);

        this.InvalidateVisual();
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        base.OnMouseMove(e);

        this.InvalidateVisual();
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);

        var width = this.ActualWidth;
        var height = this.ActualHeight;

        if (width <= 0 || height <= 0)
        {
            return;
        }

        // 绘制横向线
        this.DrawHorizontalLines(drawingContext, width, height);

        // 绘制纵向线
        this.DrawVerticalLines(drawingContext, width, height);

        // 绘制鼠标交叉线
        this.DrawMouseCrossLines(drawingContext, width, height);
    }

    private FormattedText? CreateFormattedText(string text)
    {
        var typeface = this.defaultTypeface;
        if (typeface == null)
        {
            return null;
        }

        return new FormattedText(
            text,
            CultureInfo.CurrentCulture,
            this.FlowDirection,
            typeface,
            this.FontSize,
            this.Foreground, this.pixelsPerDip);
    }

    private void DrawHorizontalLines(DrawingContext drawingContext, double width, double height)
    {
        // 横线
        for (double y = 0; y <= height; y += 10)
        {
            if (y == 0)
            {
                continue;
            }

            var currentPath = this.GetPenWithLine(y);
            drawingContext.DrawLine(currentPath, new Point(0, y), new Point(width, y));

            if (y % 50 != 0 || y == 0)
            {
                continue;
            }

            var y1 = y;
            this.DrawText(drawingContext, $"{y:0}", text => new Point(1, y1 - text.Height / 2));
        }
    }

    private void DrawMouseCrossLines(DrawingContext drawingContext, double width, double height)
    {
        // 画鼠标交叉线
        if (!this.IsMouseOver)
        {
            return;
        }

        {
            var mousePen = this.GetPen(FontWeights.DemiBold);

            var position = Mouse.GetPosition(this);
            drawingContext.DrawLine(mousePen, position with { Y = 0, }, position with { Y = height, });
            drawingContext.DrawLine(mousePen, position with { X = 0, }, position with { X = width, });

            this.DrawText(drawingContext, $"{position.X:0}", text => new Point(position.X - text.Width / 2, height - text.Height - 1));
            this.DrawText(drawingContext, $"{position.Y:0}", text => new Point(width - text.Width - 1, position.Y - text.Height / 2));
        }
    }

    private void DrawText(DrawingContext drawingContext, string text, Func<FormattedText, Point> getTextLocation)
    {
        var formattedText = this.CreateFormattedText(text);
        if (formattedText == null)
        {
            return;
        }

        var location = getTextLocation(formattedText);

        // 虚化一下背景让文字清晰一些 
        var textBounds = new Rect(location, new Size(formattedText.Width, formattedText.Height));
        drawingContext.DrawRectangle(new SolidColorBrush(Color.FromArgb(200, 255, 255, 255)), null, textBounds);

        // 绘制文字
        drawingContext.DrawText(formattedText, location);
    }

    private void DrawVerticalLines(DrawingContext drawingContext, double width, double height)
    {
        // 纵线
        for (double x = 0; x <= width; x += 10)
        {
            var currentPath = this.GetPenWithLine(x);
            drawingContext.DrawLine(currentPath, new Point(x, 0), new Point(x, height));

            if (x % 50 != 0 || x == 0)
            {
                continue;
            }

            var x1 = x;
            this.DrawText(drawingContext, $"{x:0}", text => new Point(x1 - text.Width / 2, 1));
        }
    }

    private Pen GetPen(FontWeight key)
    {
        return this.penDict.TryGetValue(key, out var pen) ? pen : this.defaultPen;
    }

    private Pen GetPenWithLine(double len)
    {
        if (len % 100 == 0)
        {
            return this.GetPen(FontWeights.ExtraBold);
        }

        return len % 50 == 0 ? this.GetPen(FontWeights.Bold) : this.GetPen(FontWeights.Normal);
    }

    private void InheritPropertyFromParent(DependencyProperty sourceProperty, DependencyProperty targetProperty)
    {
        if (this.ReadLocalValue(targetProperty) == DependencyProperty.UnsetValue &&
            this.Parent?.GetValue(sourceProperty) is { } value)
        {
            this.SetValue(targetProperty, value);
        }
    }

    private void InitPenDict()
    {
        // 定义线条画笔
        var penBrush = this.Foreground.Clone();
        penBrush.Opacity = 0.3;

        var pen = new Pen(penBrush, 1);
        pen.Freeze();

        var boldPen = pen.Clone();
        boldPen.Thickness = 1.5;
        boldPen.Freeze();

        var extraBoldPen = pen.Clone();
        extraBoldPen.Thickness = 2;
        extraBoldPen.Freeze();

        var mousePen = new Pen(this.Foreground, 1.5);
        mousePen.Freeze();

        this.penDict[FontWeights.Normal] = pen;
        this.penDict[FontWeights.Bold] = boldPen;
        this.penDict[FontWeights.ExtraBold] = extraBoldPen;
        this.penDict[FontWeights.DemiBold] = mousePen;
    }

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
        // 如果一些属性没有设置,则使用父类的属性值
        this.InheritPropertyFromParent(TextElement.ForegroundProperty, ForegroundProperty);
        this.InheritPropertyFromParent(TextElement.FontFamilyProperty, FontFamilyProperty);
        this.InheritPropertyFromParent(TextElement.FontStyleProperty, FontStyleProperty);
        this.InheritPropertyFromParent(TextElement.FontWeightProperty, FontWeightProperty);
        this.InheritPropertyFromParent(TextElement.FontStretchProperty, FontStretchProperty);
        this.InheritPropertyFromParent(TextElement.FontSizeProperty, FontSizeProperty);
        this.InheritPropertyFromParent(FlowDirectionProperty, FlowDirectionProperty);
        this.InheritPropertyFromParent(OpacityProperty, OpacityProperty);

        // 输出文字字形
        this.defaultTypeface = new Typeface(
            this.FontFamily,
            this.FontStyle,
            this.FontWeight,
            this.FontStretch);

        this.InitPenDict();
    }
}

DpiHelper.cs


public static class DpiHelper
{
    public static double GetScale(FrameworkElement element)
    {
        var source = PresentationSource.FromVisual(element);
        return source?.CompositionTarget == null ? 1.0 : source.CompositionTarget.TransformFromDevice.M11;
    }

    public static double GetScale(Window window)
    {
        return GetScale(window as FrameworkElement);
    }

    public static double GetScale(Control control)
    {
        return GetScale(control as FrameworkElement);
    }
}
上一篇
下一篇