2010-01-18 12 views
8

Usando WPF, tengo un control ListBox con un DataTemplate dentro de él. El código XAML relevante se muestra a continuación:Edición en línea TextBlock en un ListBox con plantilla de datos (WPF)

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

Lo que estoy tratando de hacer es hacer que el TextBlock responden a un (doble) haga clic en el que la convierte en una TextBox. El usuario puede editar la descripción y presionar regresar o cambiar el foco para realizar el cambio.

He intentado agregar un elemento TextBox en la misma posición que el TextBlock y haciendo su visiblity de Collapsed, pero no sé cómo navegar hacia la derecha TextBox cuando el usuario ha hecho clic en un TextBlock. Es decir, sé que el usuario ha hecho clic en un determinado TextBlock, ahora queTextBox ¿muestro?

Cualquier ayuda sería muy apreciada,

-Ko9

+0

Como consejo, en lugar de usar las etiquetas 'pre' y los corchetes angulares que se escapan explícitamente, puede simplemente pegar XAML directamente en el editor y usar el botón 101010 para formatearlo como código. – itowlson

Respuesta

14

Lo que he hecho en estas situaciones se usa la jerarquía XAML para determinar qué elemento mostrar/ocultar. Algo a lo largo de las líneas de:

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

con el código:

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

Siempre a su vez cosas como esta que voy a volver a utilizar en un UserControl, que puedo agregar control de errores adicional para, y garantice que el Grid solo contendrá dos elementos, y el orden de los mismos nunca cambiará.

EDIT: Además, convertir esto en un control de usuario le permite crear una propiedad Text para cada instancia, por lo que puede nombrar cada uno y hacer referencia al texto directamente sin pesca para el valor de la corriente a través de la ((TextBox)myGrid.Children[1]).Text fundición.Esto hará que tu código sea mucho más eficiente y limpio. Si lo convierte en un UserControl, también puede nombrar los elementos TextBlock y TextBox, por lo que no se necesita ningún casting.

+0

esto puede sonar extraño, podría editar su respuesta ... Hice clic de forma prematura, luego eliminé mi voto, porque no estaba seguro de si esto era lo que buscaba. Luego traté de publicar una votación nuevamente porque era lo que necesitaba y Stack Overflow dice que no puedo votar en esta publicación a menos que se haya editado. de ahí la solicitud. Saludos, publicación muy útil como resultó. –

4

La forma ideal de hacer esto sería la creación de un control ClickEditableTextBlock, que por defecto hace como un TextBlock pero muestra un cuadro de texto cuando el usuario hace clic eso. Como cualquier ClickEditableTextBlock dado tiene solo un TextBlock y un TextBox, no tiene el problema correspondiente. Luego utiliza un ClickEditableTextBlock en lugar de separar TextBlocks y TextBoxes en su DataTemplate.

Esto tiene el beneficio adicional de encapsular la funcionalidad en un control para que no contamine el código subyacente de la ventana principal con el comportamiento de edición, además de que puede reutilizarlo fácilmente en otras plantillas.


Si esto suena como demasiado esfuerzo, puede utilizar la etiqueta o de una propiedad asociada a asociar cada TextBlock con un cuadro de texto:

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

Nota el uso de {Binding ElementName=tb} en la etiqueta para referirse a la TextBox llamado tb.

Y en el código subyacente:

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 
} 

(Para evitar el uso de la propiedad Tag repugnante, se podría definir una propiedad adjunta a medida para llevar a enlazar el cuadro de texto, pero por razones de brevedad no estoy mostrando eso.)

11

Consulte el fragmento de código de Nathan Wheeler, los siguientes códigos son fuente completa de UserControl que codifiqué ayer. Especialmente, se tratan problemas de encuadernación. El código de Nathan es fácil de seguir, pero necesita ayuda para trabajar con texto con datos.

ClickToEditTextboxControl.xaml.cs

public partial class ClickToEditTextboxControl : UserControl 
{ 
    public ClickToEditTextboxControl() 
    { 
     InitializeComponent(); 
    } 

    public string Text 
    { 
     get { return (string)GetValue(TextProperty); } 
     set { SetValue(TextProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty TextProperty = 
     DependencyProperty.Register("Text", typeof(string), typeof(ClickToEditTextboxControl), new UIPropertyMetadata()); 

    private void textBoxName_LostFocus(object sender, RoutedEventArgs e) 
    { 
     var txtBlock = (TextBlock)((Grid)((TextBox)sender).Parent).Children[0]; 

     txtBlock.Visibility = Visibility.Visible; 
     ((TextBox)sender).Visibility = Visibility.Collapsed; 
    } 

    private void textBlockName_MouseDown(object sender, MouseButtonEventArgs e) 
    { 
     var grid = ((Grid) ((TextBlock) sender).Parent); 
     var tbx = (TextBox)grid.Children[1]; 
     ((TextBlock)sender).Visibility = Visibility.Collapsed; 
     tbx.Visibility = Visibility.Visible; 

     this.Dispatcher.BeginInvoke((Action)(() => Keyboard.Focus(tbx)), DispatcherPriority.Render); 
    } 

    private void TextBoxKeyDown(object sender, KeyEventArgs e) 
    { 
     if (e == null) 
      return; 

     if (e.Key == Key.Return) 
     { 
      TextBoxLostFocus(sender, null); 
     } 
    } 
} 

ClickToEditTextboxControl.xaml

<UserControl x:Class="Template.ClickToEditTextboxControl" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     mc:Ignorable="d" 
     Name="root" 
     d:DesignHeight="30" d:DesignWidth="100"> 
<Grid> 
    <TextBlock Name="textBlockName" Text="{Binding ElementName=root, Path=Text}" VerticalAlignment="Center" MouseDown="textBlockName_MouseDown" /> 
    <TextBox Name="textBoxName" Text="{Binding ElementName=root, Path=Text, UpdateSourceTrigger=PropertyChanged}" Visibility="Collapsed" LostFocus="textBoxName_LostFocus" KeyDown ="TextBoxKeyDown"/> 
</Grid> 
</UserControl> 

Y, por último, puede utilizar este control en el XAML de la siguiente manera:

<Template1:ClickToEditTextboxControl Text="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" MinWidth="40" Height="23" /> 

nota que Modo = TwoWay, UpdateSourceTrigger = PropertyChanged está establecido. Permite cambiar el valor enlazado en cada tipo.

Cuestiones relacionadas