You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF自定义水印TextBox调用Focus()获焦后不显示输入光标问题

问题原因

这个光标不显示的问题核心是自定义模板的焦点路由配置错误:
你的模板在目标TextBox内部又嵌套了一层名为inputText的TextBox作为实际输入载体,外层Border上设置的FocusManager.FocusedElement绑定不会在代码调用外层Focus()方法时,自动将键盘焦点传递给内部的输入框。
你看到背景变黄,是因为外层作为样式目标的TextBox拿到了逻辑焦点,触发了IsFocused=True对应的样式触发器,但实际承载输入逻辑的内部inputText没有拿到键盘焦点,自然不会显示输入光标。此时能输入内容只是WPF路由文本输入事件冒泡到内部文本框导致的异常表现,不是正常行为。

存在的配置问题
  • 模板结构冗余:在TextBox的ControlTemplate中嵌套另一层TextBox作为输入元素,本身就不符合WPF控件模板的设计规范,原生TextBox的模板预留了PART_ContentHost作为输入内容宿主,不需要额外嵌套输入控件
  • 焦点配置失效:FocusManager.FocusedElement附加属性设置在Border上,仅能在Border首次通过交互获得焦点时尝试转移焦点,无法响应代码主动调用外层TextBox.Focus()的场景
  • 触发器写法不规范:你写的焦点触发器里Property=" IsFocused"和Value=" true"前面多了无意义的空格,虽然WPF类型转换器能容错识别,但长期来看容易引发匹配异常
  • 焦点同步缺失:外层TextBox和内部嵌套的inputText没有做焦点联动,外层拿到焦点时没有主动将键盘焦点推给实际承载输入的内部控件
修复方法

最小改动修复

如果不想调整现有模板结构,只需要给模板根Border添加GotFocus事件处理,在外层获得焦点时主动把键盘焦点设置到内部输入框即可,同时修正触发器的多余空格:
首先修改XAML模板部分:

<ControlTemplate TargetType="{x:Type TextBox}">
    <!-- 移除Border上无效的FocusManager.FocusedElement绑定,添加GotFocus事件 -->
    <Border x:Name="bg" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" 
            BorderThickness="{TemplateBinding BorderThickness}" GotFocus="Bg_GotFocus">
        <Grid>
            <TextBlock Text="{TemplateBinding Tag}" VerticalAlignment="Center" Margin="8,0,0,0">
                <TextBlock.Visibility>
                    <MultiBinding Converter="{StaticResource InputToVisibility}">
                        <Binding ElementName="inputText" Path="Text.IsEmpty" />
                        <Binding ElementName="inputText" Path="IsFocused" />
                    </MultiBinding>
                </TextBlock.Visibility>
            </TextBlock>
            <TextBox Name="inputText" Background="Transparent" BorderThickness="0"
                    Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text, UpdateSourceTrigger=PropertyChanged}"
                    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" 
                    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" />
        </Grid>
    </Border>
    <ControlTemplate.Triggers>
        <!-- 修正属性和值前的多余空格 -->
        <Trigger Property="IsFocused" Value="True">
            <Setter TargetName="bg" Property="BorderThickness" Value="5" />
            <Setter TargetName="bg" Property="Background" Value="Yellow" />
        </Trigger>
        <Trigger Property="IsFocused" Value="False">
            <Setter TargetName="bg" Property="Background" Value="red" />
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

添加后台事件处理代码:

private void Bg_GotFocus(object sender, RoutedEventArgs e)
{
    if (sender is not Border border) return;
    if (border.FindName("inputText") is not TextBox innerTextBox) return;
    // 主动将键盘焦点设置到内部输入框,光标即可正常显示
    innerTextBox.Focus();
    Keyboard.Focus(innerTextBox);
    e.Handled = true;
}

推荐规范写法

建议移除嵌套的TextBox,按照WPF TextBox的模板约定使用PART_ContentHost作为输入宿主,从根源上避免焦点异常,性能也更好:

<ControlTemplate TargetType="{x:Type TextBox}">
    <Border x:Name="bg" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" 
            BorderThickness="{TemplateBinding BorderThickness}">
        <Grid>
            <TextBlock Text="{TemplateBinding Tag}" VerticalAlignment="Center" Margin="8,0,0,0">
                <TextBlock.Visibility>
                    <MultiBinding Converter="{StaticResource InputToVisibility}">
                        <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Text.IsEmpty" />
                        <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="IsFocused" />
                    </MultiBinding>
                </TextBlock.Visibility>
            </TextBlock>
            <!-- 用约定的PART_ContentHost替代嵌套TextBox,原生输入逻辑自动生效 -->
            <ScrollViewer x:Name="PART_ContentHost" Margin="8,0,0,0"
                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
        </Grid>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsFocused" Value="True">
            <Setter TargetName="bg" Property="BorderThickness" Value="5" />
            <Setter TargetName="bg" Property="Background" Value="Yellow" />
        </Trigger>
        <Trigger Property="IsFocused" Value="False">
            <Setter TargetName="bg" Property="Background" Value="red" />
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

这种写法不需要额外写后台焦点处理代码,调用Focus()方法时光标会正常显示,水印逻辑也能正常工作。

内容的提问来源于stack exchange,提问作者HimorriveL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 16:09:21