-1

I have a two-way databound TextBox that I need to remain editable, but conditionally also append an additional non-editable string to. Is there any way of achieving this?

I'm not talking about defining a string mask, I'm talking about appending "Ghost text" to the end of a user-provided string.

Something along the lines of this:

Ghost Text in textbox

(I want the user to edit the bit in black, and the grey bit to be appended at the end after they finish their edits, but to not be editable in and of itself)

For additional clarity, this is the closest example that I could think of that is similar. I don't want to explain my exact problem domain, but it does map quite nicely to the concept of providing a new directory on an existing provided user directory.

As per the question title, I was considering adjusting the position of a TextBlock to the end of the TextBox text, and doing something like this answer proposes, but that means that I'll have to both figure out where the textbox text ends, and crop the TextBlock so it doesn't overflow the TextBox.

I tried this with a RichTextBox, but that has its own problems to solve. <Run IsEnabled="True"/> doesn't appear to work how I want, and if the editable and non-editable portion are different colours, they can end up merging and its easy to accidentally remove the user entered portion.

These are just things I have tried, I've googled around and found no promising alternatives, can anyone here suggest anything?

Andy Wynn
  • 1,171
  • 7
  • 11
  • https://github.com/xceedsoftware/wpftoolkit/wiki/MaskedTextBox should do the trick. Or this one https://www.c-sharpcorner.com/UploadFile/mahesh/wpf-maskedtextbox-control/ – Firo Jan 16 '23 at 09:15
  • 1
    Is there a particular downside to showing/hiding a textblock? Text appearing inside the editable part would seem a bit weird to me. – Andy Jan 16 '23 at 09:46
  • @Firo I'm not looking to mask the textbox and have it be user-replaceable. I'm not sure how to apply this to the question. – Andy Wynn Jan 16 '23 at 13:11
  • @Andy No problem with that, but see my edits for an idea of what I'm trying to create – Andy Wynn Jan 16 '23 at 13:12
  • 1
    I see. I think that clarifies your question then. I've voted to re open it. I would use a textblock to measure the text the user entered. Make the background brush of the textbox a visualbrush. In that goes 2 textblocks in a horizontal stackpanel. One has transparent foreground and binds to the text property. The other has grey and will of course move right as the user enters text. You would probably need to fiddle round with padding on the first textblock to get it to match the textbox text as entered. – Andy Jan 16 '23 at 13:19
  • @Andy Thanks for this comment, those are some excellent suggestions. I'll have a go and report back. – Andy Wynn Jan 16 '23 at 13:36
  • @AndyWynn the masked textbox can have fixed parts in the text. This can be used to display currency and the like. – Firo Jan 16 '23 at 14:16
  • @Firo I understand what masking is, but I wanted ghost text, not masked text. Masks are designed to be overwritten, whereas I need this to be appended but otherwise not interactable. – Andy Wynn Jan 16 '23 at 15:10
  • @Andy, do you want to add your solution so I can give you the rep for pointing me in the right direction? – Andy Wynn Jan 16 '23 at 15:19
  • 1
    I posted an answer. I'm not so bothered about the rep as providing a solution might help someone else FWIW. – Andy Jan 16 '23 at 16:37

3 Answers3

1

Here's my suggestion which might be useful for someone else searching SO.

It's an approach very similar to what I've used for watermarks.

<Grid>
    <TextBox HorizontalAlignment="Left"
             VerticalAlignment="Top"
             Width="200">
        <TextBox.Background>
            <VisualBrush Stretch="None"
                         AlignmentX="Left">
                <VisualBrush.Visual>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Text, RelativeSource={RelativeSource AncestorType=TextBox}}"
                                   Foreground="Transparent"
                                   />
                        <TextBlock Text="\Something\Or\Other"
                                   Foreground="Gray"
                                   Margin="2,4,2,6"
                                   />
                    </StackPanel>
                </VisualBrush.Visual>
            </VisualBrush>
        </TextBox.Background>
    </TextBox>
</Grid>

A certain amount of fiddling about with the padding is necessary to get it to look right. This looks near enough to me on my monitor even if the picture might look slightly off.

enter image description here

Using the background brush sort of saves complexity of stacking controls and you could encapsulate some behaviour just in a style.

Extending using an attached property to compose functionality if you wanted say a watermarktextbox.

Andy
  • 11,864
  • 2
  • 17
  • 20
0
<Grid>
    <TextBox Text="{Binding Whatever}" x:Name="txtBox"/>
    <TextBlock IsHitTestVisible="False">
        <Run Text="{Binding Text, ElementName=txtBox}" Foreground="{x:Null}"/>
        <Run Text=" a ghost text" Foreground="Gray"/>
    </TextBlock>
</Grid>
Firo
  • 30,626
  • 4
  • 55
  • 94
0

The below code solved my issue. I needed to use TextBox controls for the values to line up consistently, as there are default styles on all controls.

<Grid>
    <TextBox
        HorizontalAlignment="Stretch"
        VerticalAlignment="Center"
        Text="{Binding ControlEntry, UpdateSourceTrigger=PropertyChanged}"
        Visibility="Visible" />
    <StackPanel Orientation="Horizontal">
        <TextBox
            Margin="0"
            Padding="0"
            VerticalAlignment="Center"
            Text="{Binding ControlEntry}"
            Visibility="Hidden" />
        <TextBox
            Margin="0"
            Padding="-4,0,0,0"
            VerticalAlignment="Center"
            Background="Transparent"
            BorderThickness="0"
            Foreground="DimGray"
            IsEnabled="False"
            IsHitTestVisible="False"
            Text="{Binding ControlEntrySuffix}" />
    </StackPanel>
</Grid>
Andy Wynn
  • 1,171
  • 7
  • 11