Встроенное редактирование TextBlock в ListBox с Шаблоном Данных (WPF)

Используя WPF, у меня есть a ListBox управление с a DataTemplate в нем. Соответствующий код XAML показывают ниже:

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

То, что я пытаюсь сделать, делают TextBlock ответьте на (двойной) щелчок, который превращает его в a TextBox. Пользователь может затем отредактировать описание, и нажать возврат или изменить фокус для внесения изменения.

Я попытался добавить a TextBox элемент в той же позиции TextBlock и создания его видимости Collapsed, но я не знаю, как перейти направо TextBox когда пользователь нажал на a TextBlock. Таким образом, я знаю, что пользователь нажал на определенное TextBlock, теперь, который TextBox я показываю?

Любая справка ценилась бы значительно,

- Ko9

9
задан Dave Clemmer 16 September 2011 в 17:40
поделиться

2 ответа

То, что я сделал в этих ситуациях, используется иерархия XAML для определения того, какой элемент показывать/скрывать. Что-то вроде:

<Grid>
  <TextBlock MouseDown="txtblk_MouseDown" />
  <TextBox LostFocus="txtbox_LostFocus" Visibility="Collapsed" />
</Grid>

с кодом:

protected void txtblk_MouseDown(object sender, MouseButtonEventArgs e)
{
    TextBox txt = (TextBox)((Grid)((TextBlock)sender).Parent).Children[1];
    txt.Visibility = Visibility.Visible;
    ((TextBlock)sender).Visibility = Visibility.Collapsed;
}

protected void txtbox_LostFocus(object sender, RoutedEventArgs e)
{
    TextBlock tb = (TextBlock)((Grid)((TextBox)sender).Parent).Children[0];
    tb.Text = ((TextBox)sender).Text;
    tb.Visibility = Visibility.Visible;
    ((TextBox)sender).Visibility = Visibility.Collapsed;
}

Я всегда превращаю такие вещи, которые собираюсь использовать повторно, в UserControl, к которому я могу добавить дополнительную обработку ошибок, и гарантировать, что Grid будет содержать только два элемента, и их порядок никогда не изменится.

EDIT: Дополнительно, превращая это в UserControl, вы можете создать свойство Text для каждого инстанцирования, так что вы можете назвать каждое из них и напрямую ссылаться на текст, не подбирая текущее значение с помощью кастинга ((TextBox)myGrid.Children[1]).Text. Это сделает Ваш код намного более эффективным и чистым. Если вы сделаете его в UserControl, вы также можете назвать элементы TextBlock и TextBox, так что кастинг вообще не нужен.

15
ответ дан 4 December 2019 в 06:57
поделиться

Идеальным способом сделать это было бы создание элемента управления ClickEditableTextBlock, который по умолчанию отображается как TextBlock, но показывает TextBox, когда пользователь на него нажимает. Поскольку любой данный элемент управления ClickEditableTextBlock имеет только один TextBlock и один TextBox, у вас нет проблемы с совпадением. Тогда вы используете ClickEditableTextBlock вместо отдельных TextBlock и TextBox в DataTemplate.

Это имеет дополнительное преимущество, заключающееся в инкапсуляции функциональности в элемент управления, чтобы вы не загрязняли код главного окна - за поведением редактора, плюс вы можете легко повторно использовать его в других шаблонах.


Если это звучит как слишком много усилий, вы можете использовать Tag или прикрепленное свойство для ассоциирования каждого TextBlock с TextBox:

<DataTemplate>
  <StackPanel>
    <TextBlock Text="whatever"
               MouseDown="TextBlock_MouseDown"
               Tag="{Binding ElementName=tb}" />
    <TextBox Name="tb" />
  </StackPanel>
</DataTemplate>

Обратите внимание на использование {Binding ElementName=tb} на Tag для ссылки на TextBox с именем tb.

А в вашем коде позади:

private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
  FrameworkElement textBlock = (FrameworkElement)sender;
  TextBox editBox = (TextBox)(textBlock.Tag);
  editBox.Text = "Wow!";  // or set visible or whatever
}

(Чтобы избежать использования неприятного свойства Tag, вы могли бы определить пользовательское прикрепленное свойство для переноса привязки TextBox, но для краткости я этого не показываю)

.
4
ответ дан 4 December 2019 в 06:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: