【问题标题】:Show and focus TextBox in DataTemplate在 DataTemplate 中显示和聚焦 TextBox
【发布时间】:2025-12-30 22:15:11
【问题描述】:

我搜索了高低,但我无法弄清楚这一点。我正在构建一个具有可编辑项目的ListBox。我有一个DataTemplate 用于ListBox.ItemTemplate,其中包含(除其他外)TextBlockTextBoxTextBlock 始终可见,TextBox 仅在用户双击 TextBlock 后可见。当用户单击列表中的另一个项目时,TextBox 再次隐藏以显示TextBlock。所有这一切都很好。查看我的代码:

XAML

<Window.Resources>
   <local:GoalCollection x:Key="goals"/>
   <DataTemplate x:Key="GoalItemTemplate" DataType="local:Goal">
      <Grid>
         <TextBlock Text="{Binding Title}"
                    MouseLeftButtonDown="TextBlock_MouseLeftButtonDown"
                    VerticalAlignment="Center"/>
         <TextBox Name="EntryBox"
                  Text="{Binding Title}"
                  Visibility="Hidden"
                  BorderBrush="{x:Null}"
                  Padding="-2,0,0,0"
                  Panel.ZIndex="1"
                  Margin="-2,0,0,0"/>
      </Grid>
   </DataTemplate>
</Window.Resources>
<Grid>
   <Grid.ColumnDefinitions>
      <ColumnDefinition />
      <ColumnDefinition Width="2*" />
   </Grid.ColumnDefinitions>
   <ListBox Name="GoalsList"
      ItemsSource="{Binding Source={StaticResource goals}}"
      HorizontalContentAlignment="Stretch"
      ItemTemplate="{StaticResource GoalItemTemplate}"
      SelectionChanged="GoalsList_SelectionChanged" />
</Grid>

C#

public partial class MainWindow : Window
{
    GoalCollection goals;
    public MainWindow()
    {
       InitializeComponent();
    }

    private childItem FindVisualChild<childItem>(DependencyObject obj)
    where childItem : DependencyObject { ... }

    protected override void OnInitialized(EventArgs e)
    {
       base.OnInitialized(e);
       goals = (GoalCollection)Resources["goals"];
    }

    private void TextBlock_MouseLeftButtonDown(object sender, 
                                               MouseButtonEventArgs e)
    {
       if (e.ClickCount == 2)
       {
          TextBlock tblk = sender as TextBlock;
          if (tblk == null) 
             return;
          TextBox tbx = ((Grid)tblk.Parent).FindName("EntryBox") as TextBox;
          if (tbx == null) 
             return;
          tbx.Visibility = Visibility.Visible;
          Keyboard.Focus(tbx);
       }
    }

    private void GoalsList_SelectionChanged(object sender, 
                                            SelectionChangedEventArgs e)
    {
       ListBoxItem lbi;
       ContentPresenter cp;
       DataTemplate dt;
       TextBox tbx;

       foreach (Goal item in e.RemovedItems)
       {
          lbi = (ListBoxItem)GoalsList.ItemContainerGenerator.
                                       ContainerFromItem(item);
          cp = FindVisualChild<ContentPresenter>(lbi);
          dt = cp.ContentTemplate;
          tbx = (TextBox)dt.FindName("EntryBox", cp);
          if (tbx == null) 
             continue;
          tbx.Visibility = Visibility.Hidden;
       }
    }
 }

我遇到的问题是TextBox 在双击后立即将焦点转移回主机ListBoxItem。需要额外(第三次)单击才能将焦点放在 TextBox 上。

通过这个,我发现TextBox 确实获得了焦点。但随后它立即丢失(尝试为TextBox.LostKeyboardFocus 事件添加处理程序并逐步退出“TextBlock_MouseLeftButtonDown()”方法)。有什么想法吗?

谢谢。

【问题讨论】:

    标签: wpf textbox focus visibility


    【解决方案1】:

    我的猜测是点击事件正在冒泡到 ListBox 并且它通过选择项目来处理它。

    尝试将此添加到您的 Click 事件处理程序(在 Keyboard.Focus(tbx); 之后)

    e.Handled = true;
    

    【讨论】:

    • 您,先生,真是个天才!我一直忘记这件事。
    【解决方案2】:

    如果您想将焦点放在子元素上,请尝试使用 FocusManager。

    <DataTemplate x:Key="MyDataTemplate" DataType="ListBoxItem">
       <Grid>
          <WrapPanel Orientation="Horizontal" 
                     FocusManager.FocusedElement="{Binding ElementName=tbText}">
             <CheckBox IsChecked="{Binding Path=Completed}" Margin="5" />
             <Button Style="{StaticResource ResourceKey=DeleteButtonTemplate}" 
                     Margin="5" Click="btnDeleteItem_Click" />
             <TextBox Name="tbText" 
                      Text="{Binding Path=Text}" 
                      Width="200" 
                      TextWrapping="Wrap" 
                      AcceptsReturn="True" 
                      Margin="5" 
                      Focusable="True"/>
             <DatePicker Text="{Binding Path=Date}" Margin="5"/>
          </WrapPanel>
       </Grid>
    </DataTemplate>
    

    【讨论】: