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>
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.
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! :)
I was able to resolve the issue by making the style of TextBox Stretched as follows:
<Style x:Name="TextBoxStyleTitle"
TargetType="TextBox">
<Setter Property="VerticalAlignment"
Value="Stretch"></Setter>
<Setter Property="HorizontalAlignment"
Value="Stretch"></Setter>
</Style>