Using WPF, I have a ListBox
control with a DataTemplate
inside it. The relevant XAML code is shown below:
<ListBox Name="_todoList" Grid.Row="1" BorderThickness="2"
Drop="todoList_Drop" AllowDrop="True"
HorizontalContentAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
AlternationCount="2">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0" Checked="CheckBox_Check" />
<TextBlock Name="descriptionBlock"
Grid.Column="1"
Text="{Binding Description}"
Cursor="Hand" FontSize="14"
ToolTip="{Binding Description}"
MouseDown="TextBlock_MouseDown" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
What I am trying to do is make the TextBlock
respond to a (double)click which turns it into a TextBox
. The user can then edit the description, and press return or change focus to make the change.
I have tried adding a TextBox
element in the same position as the TextBlock and making its visiblity Collapsed
, but I don't know how to navigate to the right TextBox
when the user has clicked on a TextBlock
. That is, I know the user has clicked on a certain TextBlock
, now which TextBox
do I show?
Any help would be appreciated greatly,
-Ko9
The ideal way to do this would be to create a
ClickEditableTextBlock
control, which by default renders as a TextBlock but shows a TextBox when the user clicks it. Because any given ClickEditableTextBlock has only one TextBlock and one TextBox, you don't have the matching issue. Then you use a ClickEditableTextBlock instead of separate TextBlocks and TextBoxes in your DataTemplate.This has the side benefit of encapsulating the functionality in a control so you don't pollute your main window code-behind with the edit behaviour, plus you can easily reuse it in other templates.
If this sounds like too much effort, you can use Tag or an attached property to associate each TextBlock with a TextBox:
Note the use of
{Binding ElementName=tb}
on the Tag to refer to the TextBox namedtb
.And in your code-behind:
(To avoid the use of the nasty Tag property, you could define a custom attached property to carry the TextBox binding, but for brevity I'm not showing that.)
What I've done in these situations is used the XAML hierarchy to determine which element to show/hide. Something along the lines of:
with the code:
I always turn stuff like this that I'm going to reuse into a
UserControl
, which I can add additional error handling to, and guarantee that theGrid
will only contain two items, and the order of them will never change.EDIT: Additionally, turning this into a UserControl allows you to create a
Text
property for each instantiation, so you can name each one and reference the text directly without fishing for the current value through the((TextBox)myGrid.Children[1]).Text
casting. This will make your code much more efficient and clean. If you make it into a UserControl, you can also name theTextBlock
andTextBox
elements, so no casting is needed at all.Refer to the Nathan Wheeler's code snippet, the following codes are complete UserControl source that I coded yesterday. Especially, Binding issues are addressed. Nathan's code is easy to follow, but needs some aid in order to work with databound text.
ClickToEditTextboxControl.xaml.cs
ClickToEditTextboxControl.xaml
And, finally, you can use this control in the XAML as below:
Note that Mode=TwoWay, UpdateSourceTrigger=PropertyChanged is set. It enables to change the binded value in every type.
If I may supplement, in order to cover the (double) part of the original question, in Youngjae's reply you make the following replacement in the xaml file:
is replaced with
adding also the proper RoutedCommand in UserControl.Resources
and a CommandBinding in UserControl.CommandBindings
Also in the code behind file:
is replaced by
In case some people want to have left doubleclick as input gesture, like I did...