0

I need to apply stroke (outline around text) to a textbox.
just like this

I have tried some solutions:

  1. Edit the template of a textbox, using OutlinedTextblock (a custom control to draw the outlined text), but I can't select characters;
  2. apply shader effect, but it didn't work well. The stroke looked ugly.

Do you have any good solution?

EldHasp
  • 6,079
  • 2
  • 9
  • 24
Ren Lei
  • 1
  • 1
  • Why dont just wrap the textbox in a border? ` ` – bazsisz Feb 06 '23 at 07:49
  • I need outline around the text, not a border around the control (TextBox supports border itself). – Ren Lei Feb 06 '23 at 07:58
  • It is not clear what "around the text, but not the element" means. Could you show a picture, a screenshot of the result you want? – EldHasp Feb 06 '23 at 08:16
  • 1
    I think around the text is pretty clear and means around each character. Like this https://i.imgur.com/73rJR4i.png A search on outlined textblock easily finds https://stackoverflow.com/questions/93650/apply-stroke-to-a-textblock-in-wpf – Andy Feb 06 '23 at 08:36
  • You can't select characters because the text is no longer the usual things make up text. It's a path, so you can outline it. There's a fundamental difference between the characters in a textbox and a path. I would consider using a visualbrush as the background of the textbox. Do the outlining in there. Make the text in the textbox transparent. – Andy Feb 06 '23 at 08:52
  • Do you really need to edit in the same control? – Andy Feb 06 '23 at 10:03

1 Answers1

1

I did a bit of experimentation and a proof of concept.

The result isn't too bad but needs a bit of work.

When I select text the blue area isn't in exactly the right place.

This works using the approach I mentioned in comments.

The text in the textbox is transparent and the background of the control is used to show the formatted text.

My windows just has this in it:

<Grid>
    <TextBox Width="200" Height="40"
             VerticalAlignment="Top"
             TextChanged="TextBox_TextChanged"
             Foreground="Transparent"
             FontSize="32"
             >
        <TextBox.Background>
            <DrawingBrush Stretch="None"
                          AlignmentX="Left">
            <DrawingBrush.Drawing>
               <DrawingGroup>
                    <GeometryDrawing Brush="LightBlue" 
                                 x:Name="TextGeometryDrawing"
                                 Geometry="{x:Null}"
                                 >
                        <GeometryDrawing.Pen>
                        <Pen Thickness="1" Brush="Red"/>
                    </GeometryDrawing.Pen>
                </GeometryDrawing>
                 </DrawingGroup>
            </DrawingBrush.Drawing>
            </DrawingBrush>
        </TextBox.Background>
    </TextBox>
</Grid>

My textchanged handler creates the geometry:

    private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        TextBox tb = (TextBox)sender;

        FormattedText formattedText = new FormattedText(tb.Text, CultureInfo.GetCultureInfo("en-us"),
            FlowDirection.LeftToRight, new Typeface("Segoe UI"), 32, Brushes.Black,
            VisualTreeHelper.GetDpi(this).PixelsPerDip);

        Geometry geom  = formattedText.BuildGeometry(new System.Windows.Point(0, 0));
        TextGeometryDrawing.Geometry = geom;
    }

Looks like

enter image description here

An alternative is the approach I use in our map and scenario editors. I create a geometry per letter a-z and then use a horizontal itemscontrol with a path per letter using those geometries.

You can then control each letter precisely. If you don't like some aspect of what you get from truetype conversion you can edit it in inkscape.

The insertion point and selection is likely to get even more offset that way though.

Andy
  • 11,864
  • 2
  • 17
  • 20