Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Show and focus TextBox in DataTemplate

I have searched high and low, but I can't figure this one out. I am building a ListBox that has editable items. I have a DataTemplate for the ListBox.ItemTemplate that contains (among other things) a TextBlock and a TextBox. The TextBlock is always visible, and the TextBox is only visible after the user double-clicks on the TextBlock. When the user clicks another item in the list, the TextBox hides again to show the TextBlock. All of this works great. See my code:

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;
       }
    }
 }

The problem that I'm having is that the TextBox immediately shifts focus back to the host ListBoxItem after the double-click. An additional (third) click is required to focus on the TextBox.

Tracing through this, I have found that the TextBox does indeed receive focus. But then it immediately loses it (try adding a handler for the TextBox.LostKeyboardFocus event and step through and out of the `TextBlock_MouseLeftButtonDown()' method). Any ideas?

Thanks.

like image 791
gregsdennis Avatar asked Dec 09 '25 19:12

gregsdennis


2 Answers

My guess is that the click event is bubbling up to the ListBox and it's handling it by selecting the item.

Try adding this to your Click event handler (after Keyboard.Focus(tbx);)

e.Handled = true;
like image 131
Adam Barney Avatar answered Dec 12 '25 18:12

Adam Barney


If you want to give focus to a child element, try the 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>
like image 43
Jesse Seger Avatar answered Dec 12 '25 18:12

Jesse Seger



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!