简体   繁体   中英

Drawing Geometry inside Grid C# WPF

I'm trying to create an outlined text, I've tried using the ShadowEffect on the Label control but it doesn't provide the desired results and it's quite laggy, the only solution I've found so far is visualizing a Geometry build from a FormattedText object.

But that also seems to have some problems with it, namely I need to know the Width and the Height of the Control, but for example in a Grid with RowDefinitions, where their size is set to * , I cant seem to find a way to obtain the size of the control, thus my Geometry is not being displayed.

Here is my custom control class:

public class OutlinedLabel : FrameworkElement
{
    private string _text;
    public string Text
    {
        get => _text;
        set
        {
            _text = value;
            UpdateLayout();
            InvalidateArrange();
            InvalidateMeasure();
            InvalidateVisual();
        }
    }

    public Brush TextColor
    {
        get => (Brush)GetValue(TextColorProperty);
        set => SetValue(TextColorProperty, value);
    }

    public static readonly DependencyProperty TextColorProperty =
        DependencyProperty.Register(nameof(TextColor), typeof(Brush), typeof(OutlinedLabel),
            new PropertyMetadata(null));

    public TextAlignment TextAlignment
    {
        get => (TextAlignment)GetValue(TextAlignmentProperty);
        set => SetValue(TextAlignmentProperty, value);
    }

    public static readonly DependencyProperty TextAlignmentProperty =
        DependencyProperty.Register(nameof(TextAlignment), typeof(TextAlignment), typeof(OutlinedLabel),
            new PropertyMetadata(null));

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

    public static readonly DependencyProperty FontWeightProperty =
        DependencyProperty.Register(nameof(FontWeight), typeof(FontWeight), typeof(OutlinedLabel),
            new PropertyMetadata(null));


    public Brush OutlineBrush
    {
        get => (Brush)GetValue(OutlineBrushProperty);
        set => SetValue(OutlineBrushProperty, value);
    }

    public static readonly DependencyProperty OutlineBrushProperty =
        DependencyProperty.Register(nameof(OutlineBrush), typeof(Brush), typeof(OutlinedLabel),
            new PropertyMetadata(null));

    public double Thickness
    {
        get => (double)GetValue(ThicknessProperty);
        set => SetValue(ThicknessProperty, value);
    }

    public static readonly DependencyProperty ThicknessProperty =
        DependencyProperty.Register(nameof(Thickness), typeof(double), typeof(OutlinedLabel),
            new PropertyMetadata(null));

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

    public static readonly DependencyProperty FontFamilyProperty =
        DependencyProperty.Register(nameof(FontFamily), typeof(FontFamily), typeof(OutlinedLabel),
            new PropertyMetadata(null));

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

    public static readonly DependencyProperty FontSizeProperty =
        DependencyProperty.Register(nameof(FontSize), typeof(double), typeof(OutlinedLabel),
            new PropertyMetadata(null));

    public OutlinedLabel()
    {
        FontSize = 12;
        Text = string.Empty;
        FontFamily = new FontFamily("Seago UI");
        TextColor = Brushes.Black;
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        var currentOutlineBrush = OutlineBrush ?? TextColor;
        var bg_rect = new Rect(0, 0, ActualWidth, ActualHeight);
        drawingContext.DrawRectangle(Brushes.White, null, bg_rect);

        var typeface = new Typeface(FontFamily.Source);
        var formatted_text = new FormattedText(Text, new CultureInfo("en-us"), FlowDirection, typeface, FontSize,
            currentOutlineBrush);
        formatted_text.SetFontWeight(FontWeight);

        formatted_text.TextAlignment = TextAlignment;

        var origin = new Point(ActualWidth / 2, (ActualHeight - formatted_text.Height) / 2);

        var geometry = formatted_text.BuildGeometry(origin);

        var pen = new Pen(currentOutlineBrush, Thickness);
        drawingContext.DrawGeometry(TextColor, pen, geometry);
    }
}

Here is my xaml:

    <Grid Name="gridSubs" Margin="0,0,0,118" Height="Auto" VerticalAlignment="Stretch" IsHitTestVisible="false">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Viewbox StretchDirection="Both" Stretch="Uniform" Grid.Row="8">
            <customControls:OutlinedLabel TextAlignment="Center" TextColor="White" OutlineBrush="Black" FontWeight="Bold" FontSize="32" Thickness="1"/>
        </Viewbox>
        <Viewbox StretchDirection="Both" Stretch="Uniform" Grid.Row="7">
            <customControls:OutlinedLabel TextAlignment="Center" TextColor="White" OutlineBrush="Black" FontWeight="Bold" FontSize="32" Thickness="1"/>
        </Viewbox>
        //...
    </Grid>

When I set the Text property, OnRender is being invoked, but nothing visually happens, ActualWidth and ActualHeight both have a value of 0.

The class works if I create for example an OutlinedLabel not contained in a Grid.

How can I make it work in this specific case?

This OutlinedText control works. The important part is that it overrides MeasureOverride in order to determine its ActualWidth and ActualHeight .

public class OutlinedText : FrameworkElement
{
    private FormattedText text;

    public static readonly DependencyProperty TextProperty = TextBlock.TextProperty.AddOwner(
        typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).text = null) { AffectsMeasure = true });

    public static readonly DependencyProperty TextAlignmentProperty = TextBlock.TextAlignmentProperty.AddOwner(
        typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).text = null) { AffectsRender = true });

    public static readonly DependencyProperty FontSizeProperty = TextBlock.FontSizeProperty.AddOwner(
        typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).text = null) { AffectsMeasure = true });

    public static readonly DependencyProperty FontFamilyProperty = TextBlock.FontFamilyProperty.AddOwner(
        typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).text = null) { AffectsMeasure = true });

    public static readonly DependencyProperty FontStyleProperty = TextBlock.FontStyleProperty.AddOwner(
        typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).text = null) { AffectsMeasure = true });

    public static readonly DependencyProperty FontWeightProperty = TextBlock.FontWeightProperty.AddOwner(
        typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).text = null) { AffectsMeasure = true });

    public static readonly DependencyProperty FontStretchProperty = TextBlock.FontStretchProperty.AddOwner(
        typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).text = null) { AffectsMeasure = true });

    public static readonly DependencyProperty ForegroundProperty = TextBlock.ForegroundProperty.AddOwner(
        typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).text = null) { AffectsRender = true });

    public static readonly DependencyProperty BackgroundProperty = TextBlock.BackgroundProperty.AddOwner(
        typeof(OutlinedText), new FrameworkPropertyMetadata((o, e) => ((OutlinedText)o).text = null) { AffectsRender = true });

    public static readonly DependencyProperty OutlineBrushProperty = DependencyProperty.Register(
        nameof(OutlineBrush), typeof(Brush), typeof(OutlinedText),
        new FrameworkPropertyMetadata(Brushes.White, FrameworkPropertyMetadataOptions.AffectsRender, (o, e) => ((OutlinedText)o).text = null));

    public static readonly DependencyProperty OutlineThicknessProperty = DependencyProperty.Register(
        nameof(OutlineThickness), typeof(double), typeof(OutlinedText),
        new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.AffectsMeasure, (o, e) => ((OutlinedText)o).text = null));

    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    public TextAlignment TextAlignment
    {
        get { return (TextAlignment)GetValue(TextAlignmentProperty); }
        set { SetValue(TextAlignmentProperty, value); }
    }

    [TypeConverter(typeof(FontSizeConverter))]
    public double FontSize
    {
        get { return (double)GetValue(FontSizeProperty); }
        set { SetValue(FontSizeProperty, value); }
    }

    public FontFamily FontFamily
    {
        get { return (FontFamily)GetValue(FontFamilyProperty); }
        set { SetValue(FontFamilyProperty, value); }
    }

    public FontStyle FontStyle
    {
        get { return (FontStyle)GetValue(FontStyleProperty); }
        set { SetValue(FontStyleProperty, value); }
    }

    public FontWeight FontWeight
    {
        get { return (FontWeight)GetValue(FontWeightProperty); }
        set { SetValue(FontWeightProperty, value); }
    }

    public FontStretch FontStretch
    {
        get { return (FontStretch)GetValue(FontStretchProperty); }
        set { SetValue(FontStretchProperty, value); }
    }

    public Brush Foreground
    {
        get { return (Brush)GetValue(ForegroundProperty); }
        set { SetValue(ForegroundProperty, value); }
    }

    public Brush Background
    {
        get { return (Brush)GetValue(BackgroundProperty); }
        set { SetValue(BackgroundProperty, value); }
    }

    public Brush OutlineBrush
    {
        get { return (Brush)GetValue(OutlineBrushProperty); }
        set { SetValue(OutlineBrushProperty, value); }
    }

    public double OutlineThickness
    {
        get { return (double)GetValue(OutlineThicknessProperty); }
        set { SetValue(OutlineThicknessProperty, value); }
    }

    protected override Size MeasureOverride(Size availableSize)
    {
        var size = CheckText() ? new Size(text.Width, text.Height) : new Size();

        if (!double.IsNaN(Width) && size.Width < Width)
        {
            size.Width = Width;
        }

        if (!double.IsNaN(Height) && size.Height < Height)
        {
            size.Height = Height;
        }

        return size;
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        if (CheckText())
        {
            var size = DesiredSize;
            var origin = new Point();

            if (size.Width > text.Width)
            {
                if (TextAlignment == TextAlignment.Center)
                {
                    origin.X = ((size.Width - text.Width) - OutlineThickness) / 2;
                }
                else if (TextAlignment == TextAlignment.Right)
                {
                    origin.X = (size.Width - text.Width) - OutlineThickness;
                }
            }

            if (size.Height > text.Height)
            {
                origin.Y = (size.Height - text.Height) / 2;
            }

            if (Background != null)
            {
                drawingContext.DrawRectangle(Background, null, new Rect(size));
            }

            drawingContext.DrawGeometry(Foreground, new Pen(OutlineBrush, OutlineThickness), text.BuildGeometry(origin));
        }
    }

    private bool CheckText()
    {
        if (text == null)
        {
            if (string.IsNullOrEmpty(Text))
            {
                return false;
            }

            text = new FormattedText(
                Text, CultureInfo.CurrentUICulture, FlowDirection.LeftToRight,
                new Typeface(FontFamily, FontStyle, FontWeight, FontStretch),
                FontSize, Brushes.Black);
        }

        return true;
    }
}

The problem is the ViewBox . Your control needs to know/evaluate its own size in order to work within a ViewBox correctly. Just remove the ViewBox for a test and you'll see the appears. When you set Height and Width of OutlinedLabel the text also appears.

You might consider to respond to MeasureOverride and return the desired size.

In general I don't consider it best practice to use a ViewBox to overcome sizing problems of a control. The control should handle all that.

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