2010-01-18 4 views
8

Mit WPF habe ich eine ListBox Steuerung mit einer DataTemplate drin. Der entsprechende XAML-Code ist unten dargestellt:Inline-Bearbeitung TextBlock in einer ListBox mit Datenvorlage (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> 

Was ich versuche, die TextBlock reagieren auf einen (Doppel-) klicken, das macht es zu einem TextBox zu tun ist, machen. Der Benutzer kann dann die Beschreibung bearbeiten und auf Zurück oder Fokus drücken, um die Änderung vorzunehmen.

Ich habe versucht, ein TextBox Element in der gleichen Position wie der Textblock hinzufügen und seine visiblity Collapsed machen, aber ich weiß nicht, wie TextBox nach rechts zu navigieren, wenn der Benutzer auf einem TextBlock geklickt hat. Das heißt, ich weiß, der Benutzer hat auf eine bestimmte TextBlock, jetzt welcheTextBox habe ich angezeigt?

Jede Hilfe wäre sehr dankbar,

-Ko9

+0

Als Tipp, anstatt "Pre" -Tags und explizit ausbrechende spitze Klammern zu verwenden, können Sie einfach XAML direkt in den Editor einfügen und den 101010-Button verwenden, um es als Code zu formatieren. – itowlson

Antwort

14

Was ich in diesen Situationen getan habe, wird die XAML-Hierarchie verwendet, um zu bestimmen, welches Element ein-/auszublenden ist. Etwas entlang der Linien von:

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

mit dem Code:

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

ich immer Sachen wie diese drehe, dass ich in ein UserControl wieder zu verwenden werde, die ich Umgang mit zusätzlichen Fehlern hinzufügen kann, und garantieren, dass die Grid nur zwei Elemente enthält, und die Reihenfolge von ihnen wird sich nie ändern.

EDIT: Darüber hinaus können Sie in eine UserControl eine Text Eigenschaft für jede Instantiierung erstellen, so dass Sie jeden benennen und den Text direkt referenzieren können, ohne den aktuellen Wert durch die ((TextBox)myGrid.Children[1]).Text Casting zu fischen.Dadurch wird Ihr Code viel effizienter und sauberer. Wenn Sie es zu einem Benutzersteuerelement machen, können Sie auch die Elemente TextBlock und TextBox benennen, sodass keine Umwandlung erforderlich ist.

+0

das mag komisch klingen, könntest du deine antwort bearbeiten ... ich habe zu früh geklickt, dann meine stimme entfernt, weil ich mir nicht sicher war, ob das das war, wonach ich gesucht habe. Ich habe dann versucht, eine neue Abstimmung zu veröffentlichen, weil es das war, was ich brauchte, und Stack Overflow sagt, dass ich über diesen Beitrag nicht abstimmen kann, wenn er nicht bearbeitet wurde. daher die Anfrage. Prost, sehr nützlicher Beitrag, wie sich herausstellte. –

4

Der ideale Weg, dies zu tun, um eine ClickEditableTextBlock Kontrolle zu schaffen wäre, die standardmäßig als Textblock macht zeigt aber einen Text, wenn der Benutzer klickt es. Da jeder gegebene ClickEditableTextBlock nur einen TextBlock und ein TextBox hat, haben Sie nicht das passende Problem. Dann verwenden Sie einen ClickEditableTextBlock anstelle von separaten TextBlocks und TextBoxen in Ihrem DataTemplate.

Dies hat den Nebeneffekt, dass die Funktionalität in einem Steuerelement eingekapselt wird, sodass Sie das Hauptfenster nicht mit dem Bearbeitungsverhalten verschmutzen, und Sie können es problemlos in anderen Vorlagen wiederverwenden.


Wenn dies, wie zu viel Aufwand klingt, können Sie Tag oder eine angeschlossene Eigenschaft verwenden, um jede Textblock mit einem Text zuzuordnen:

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

Beachten Sie die Verwendung von {Binding ElementName=tb} am Tag auf die Bezug zu nehmen TextBox mit dem Namen tb.

Und in Ihrem Code-Behind:

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 
} 

(Um die Verwendung des bösen Tag-Eigenschaft zu vermeiden, können Sie eine benutzerdefinierte angefügte Eigenschaft definieren könnte die TextBox-Bindung, aber der Kürze halber zu tragen zeigt Ich bin nicht das.)

11

Siehe Nathan Wheeler Code-Schnipsel, die folgenden Codes sind vollständig UserControl Quelle, die ich gestern codiert. Insbesondere werden Bindungsprobleme behoben. Nathans Code ist leicht zu verstehen, benötigt aber Hilfe, um mit datengebundenem Text zu arbeiten.

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> 

Und schließlich können Sie diese Kontrolle in der XAML verwenden, wie unten:

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

Beachten Sie, dass Modus = TwoWay, UpdateSourceTrigger = PropertyChanged ist eingestellt. Es ermöglicht, den gebundenen Wert in jedem Typ zu ändern.