Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF TextBox Inside ViewBox loses Cursor on resize

I have a textbox inside a viewbox. When I try to resize the window, the textbox size and font size getting scaled, but if I try to focus the textbox and try move the cursor inside the textbox using keyboard, sometimes the cursor is getting disappeared. Is there a way to show the cursor always? Refer the below code which a TextBox inside ViewBox.

<Window x:Class="Resolution_Learning.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow">
<Viewbox Stretch="Uniform">
    <Grid Width="2560" Height="1440" >
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Label Grid.Row="0" Grid.Column="0" Content="Hello"/>
        <TextBox  Grid.Row="0" Grid.Column="1"></TextBox>
        <Label Grid.Row="0" Grid.Column="2" Content="Hello"/>
        <TextBox Grid.Row="0" Grid.Column="3"/>
        <Label Grid.Row="1" Grid.Column="0" Content="Hello"/>
        <TextBox Grid.Row="1" Grid.Column="1"/>
        <Label Grid.Row="1" Grid.Column="2" Content="Hello"/>
        <TextBox Grid.Row="1" Grid.Column="3"/>
    </Grid>
</Viewbox>

like image 496
Ayyappan Subramanian Avatar asked Dec 16 '25 17:12

Ayyappan Subramanian


2 Answers

It's a BUG in WPF. I achieved this by creating my own style for TextBox Caret.

XAML: Style for TextBox

<Style TargetType="{x:Type TextBox}" x:Key="CaretStyle" >
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <Canvas>                                    
                                <TextBox x:Name="Box" CaretBrush="Transparent"  Width="{Binding RelativeSource={RelativeSource AncestorType=TextBox},Path=ActualWidth,Mode=OneWay}" 
                                          Height="{Binding RelativeSource={RelativeSource AncestorType=TextBox},Path=ActualHeight,Mode=OneWay}"/>
                                <Border x:Name="Caret" 
                                    Visibility="Collapsed"
                                    Canvas.Left="0" Canvas.Top="0" Margin="0" Padding="0"
                                    Width="1"  Height="16"  Background="Black">                                        
                                    <Border.Triggers>
                                        <EventTrigger RoutedEvent="Border.Loaded">
                                            <BeginStoryboard>
                                                <Storyboard  x:Name="CaretStoryBoard" 
                                     RepeatBehavior="Forever">
                                                    <ColorAnimationUsingKeyFrames 
                                    Storyboard.TargetProperty="Background.Color"
                                    Duration="0:0:0:1"
                                    FillBehavior="HoldEnd">
                                                        <ColorAnimationUsingKeyFrames.KeyFrames >
                                                            <DiscreteColorKeyFrame KeyTime="0:0:0.750" 
                    Value="Transparent" />
                                                            <DiscreteColorKeyFrame KeyTime="0:0:0.000" 
                    Value="Black"/>
                                                        </ColorAnimationUsingKeyFrames.KeyFrames>
                                                    </ColorAnimationUsingKeyFrames>
                                                </Storyboard>
                                            </BeginStoryboard>
                                        </EventTrigger>                                            
                                    </Border.Triggers>
                                </Border>
                            </Canvas>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <EventSetter Event="SelectionChanged" Handler="CustomTextBox_SelectionChanged"/>
                <EventSetter Event="GotFocus" Handler="CustomTextBox_GotFocus" />
                <EventSetter Event="LostFocus" Handler="CustomTextBox_LostFocus" />
            </Style>

EVENTS: For Caret Position

 void CustomTextBox_LostFocus(object sender, RoutedEventArgs e)
    {
        var Caret = FindChild<Border>(sender as DependencyObject, "Caret");
        Caret.Visibility = Visibility.Collapsed;
    }

    void CustomTextBox_GotFocus(object sender, RoutedEventArgs e)
    {
        var Caret = FindChild<Border>(sender as DependencyObject, "Caret");
        Caret.Visibility = Visibility.Visible;
    }

    void CustomTextBox_SelectionChanged(object sender, RoutedEventArgs e)
    {
        var CustomTextBox = FindChild<TextBox>(sender as DependencyObject, "Box");
        var caretLocation = CustomTextBox.GetRectFromCharacterIndex(CustomTextBox.CaretIndex).Location;
        var Caret = FindChild<Border>(sender as DependencyObject, "Caret");
        if (!double.IsInfinity(caretLocation.X))
        {
            Canvas.SetLeft(Caret, caretLocation.X);
        }

        if (!double.IsInfinity(caretLocation.Y))
        {
            Canvas.SetTop(Caret, caretLocation.Y);
        }
    }

Helper Method: To Get Visual Child

     public static T FindChild<T>(DependencyObject parent, string childName)
   where T : DependencyObject
        {
            // Confirm parent and childName are valid. 
            if (parent == null) return null;

            T foundChild = null;

            int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < childrenCount; i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);
                // If the child is not of the request child type child
                T childType = child as T;
                if (childType == null)
                {
                    // recursively drill down the tree
                    foundChild = FindChild<T>(child, childName);

                    // If the child is found, break so we do not overwrite the found child. 
                    if (foundChild != null) break;
                }
                else if (!string.IsNullOrEmpty(childName))
                {
                    var frameworkElement = child as FrameworkElement;
                    // If the child's name is set for search
                    if (frameworkElement != null && frameworkElement.Name == childName)
                    {
                        // if the child's name is of the request name
                        foundChild = (T)child;
                        break;
                    }
                }
                else
                {
                    // child element found.
                    foundChild = (T)child;
                    break;
                }
            }

            return foundChild;
        }

Just add above Style/metods in your code and set Style for TextBoxes wherever you want and see the result. As I have created this myself without any actual measurement of actual Caret symbol, you may see a light shadow at some scale. please adjust the look & feel as needed.

like image 134
Kylo Ren Avatar answered Dec 20 '25 03:12

Kylo Ren


What I think happens here is this:

Since all the controls inside the ViewBox are re-sized, the caret is not always drawn due to pixel calculations.

To make it work, we will have to create our own caret. I've searched the web, in case someone already implemented this, and found THIS article by d.moncada.

I've downloaded the source code, and used the CustomCaretTextBox (instead of the regular TextBox) to test if it will solve the problem, and it did!

I strongly recommend you to try the solution, since it's looks elegant, works out of the box, and can be easily manipulated by your needs.

Happy Coding! :)

like image 44
Alex Pshul Avatar answered Dec 20 '25 03:12

Alex Pshul



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!