XAML Custom Textbox Cursor Stays at Start of Entry

2019-07-08 11:13发布

问题:

I'm working on creating custom controls for a XAML app for Windows 8.1 Universal/UWP and keep finding little nuances. I can't seem to find any great tutorials on creating custom implementations of existing controls (like the TextBox) so I've been looking through the source code of controls like Telerik to try and understand how their custom controls are working.

The latest issue I'm encountering is being unable to create even the most simple custom TextBox without the cursor staying at the start of the entry. As I keep entering text, the cursor stays at the beginning, even though text is getting appended to the end. I verified it's not an issue with the emulator by adding a regular textbox below it, which functions correctly. I'm sure this has something to do with how I'm creating the custom control.

Here's my control:

public class CustomTextBox : TextBox
{
    public CustomTextBox()
    {
        DefaultStyleKey = typeof(CustomTextBox);
    }
}

Here's my Generic.xaml file with the template to illustrate the issue:

<ResourceDictionary 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:customControls="using:CustomControls">
    <Style TargetType="customControls:CustomTextBox">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="customControls:CustomTextBox">
                    <TextBox Text="{TemplateBinding Text}" />
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

And finally, here's my page XAML showing the issue when you run it and enter text into the first textbox. The TextBlock is there to show the real-time changes to my entered text, which is critical to my original problem that led me here:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>
    <TextBlock Text="{Binding ElementName=txtTextBox, Path=Text}" Grid.Row="0" VerticalAlignment="Bottom" />
    <local:CustomTextBox Grid.Row="1" x:Name="txtTextBox" />
    <TextBox Grid.Row="2"></TextBox>
</Grid>

I've tried changing the binding to use TwoWay binding:

{Binding RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay, Path=Text}

I've also tried subscribing to the TextChanged event and doing a Select as a result of the new text to try and move the cursor, but neither of those have worked:

private void CustomTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    Select(Text.Length, 0);
}

Update

After changing to inherit from Control, and I've come full-circle back to my original issue. I'm unable to get the Text property to update in real-time when subscribing to the TextChanged event. I a created a new thread for that since it goes down a different path than this.

Side note: The reason I even attempted to inherit from TextBox was because that's how Telerik's RadTextBox control was implemented.

回答1:

As @lokusking mentioned, it's not right way here to nest a TextBox within another TextBox, since your CustomTextBox inherited from TextBox, you will need to customize your ControlTemplate like TextBox styles and templates.

You can copy the default template of TextBox and replace with your CustomTextBox and have a try, it will solve your problem. And also you can create you own template just for example: Create Your First WinRT WatermarkTextBox Control. But don't nest another TextBox within your CustomTextBox, you can refer the answer in this thread: Why use a templated control over a UserControl?, it well explained where and why we uses a Templated Control.

If you want to register other Properties, you can use DependencyProperty, and if you want to register new Events, you can refer to Custom events and event accessors in Windows Runtime Components.