12 Stimmen

Inline-Bearbeitung des TextBlocks in einer ListBox mit Datenvorlage (WPF)

Mit WPF habe ich eine ListBox Kontrolle mit einer DataTemplate in ihm. Der entsprechende XAML-Code ist unten dargestellt:

<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, ist, die TextBlock auf einen (Doppel-)Klick reagieren, der sie in eine TextBox . Der Benutzer kann dann die Beschreibung bearbeiten und die Eingabetaste drücken oder den Fokus ändern, um die Änderung vorzunehmen.

Ich habe versucht, eine TextBox Element an der gleichen Stelle wie der TextBlock zu platzieren und seine Sichtbarkeit Collapsed aber ich weiß nicht, wie ich nach rechts navigieren kann. TextBox wenn der Benutzer auf einen der folgenden Punkte geklickt hat TextBlock . Das heißt, ich weiß, dass der Benutzer auf eine bestimmte Seite geklickt hat. TextBlock , jetzt die TextBox zeige ich?

Für jede Hilfe wären wir Ihnen sehr dankbar,

-Ko9

16voto

Nathan Wheeler Punkte 5836

Was ich in diesen Situationen getan habe, ist die XAML-Hierarchie verwendet, um zu bestimmen, welches Element zu zeigen/auszublenden. 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 mache aus solchen Sachen, die ich wiederverwenden will, immer eine UserControl die ich mit einer zusätzlichen Fehlerbehandlung versehen kann, und garantieren, dass die Grid enthält nur zwei Einträge, deren Reihenfolge sich nicht ändert.

EDIT: Wenn Sie dies in ein UserControl umwandeln, können Sie außerdem eine Text Eigenschaft für jede Instanziierung, so dass Sie jede einzelne benennen und den Text direkt referenzieren können, ohne den aktuellen Wert über die ((TextBox)myGrid.Children[1]).Text Gießen. Dadurch wird Ihr Code viel effizienter und sauberer. Wenn Sie es zu einem UserControl machen, können Sie auch die TextBlock y TextBox Elemente, so dass überhaupt kein Guss erforderlich ist.

16voto

Youngjae Punkte 22832

Siehe Nathan Wheelers Codeschnipsel, die folgenden Codes sind vollständige UserControl-Quellen, die ich gestern codiert habe. Insbesondere werden Fragen der Bindung angesprochen. Der Code von Nathan ist leicht zu verstehen, benötigt aber einige Hilfen, 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)
        {
            textBoxName_LostFocus(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 dieses Steuerelement in der XAML wie unten dargestellt verwenden:

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

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

4voto

itowlson Punkte 72130

Der ideale Weg, dies zu tun, wäre die Erstellung einer ClickEditableTextBlock Steuerelement, das standardmäßig als TextBlock dargestellt wird, aber eine TextBox anzeigt, wenn der Benutzer darauf klickt. Da jeder beliebige ClickEditableTextBlock nur einen TextBlock und eine TextBox hat, gibt es das Problem der Übereinstimmung nicht. 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 gekapselt wird, so dass Sie den Code-Behind des Hauptfensters nicht mit dem Bearbeitungsverhalten verunreinigen, und Sie können es leicht in anderen Vorlagen wiederverwenden.


Wenn Ihnen das zu viel Aufwand ist, können Sie Tag oder eine angehängte Eigenschaft verwenden, um jeden TextBlock mit einer TextBox zu verknüpfen:

<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} auf das Tag, um auf die 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 der lästigen Tag-Eigenschaft zu vermeiden, könnten Sie eine benutzerdefinierte angehängte Eigenschaft definieren, um die TextBox-Bindung zu übernehmen, aber der Kürze halber zeige ich das nicht).

1voto

tombobadil Punkte 124

Wenn ich ergänzen darf, um die (doppelt) Teil der ursprünglichen Frage, in der Antwort von Youngjae nehmen Sie die folgende Ersetzung in der xaml-Datei vor:

<TextBlock Name="textBlockName" Text="{Binding ElementName=root, Path=Text}" VerticalAlignment="Center" MouseDown="textBlockName_MouseDown" />

wird ersetzt durch

<TextBlock Name="textBlockName" Text="{Binding ElementName=root, Path=Text}" VerticalAlignment="Center" >
    <TextBlock.InputBindings>
        <MouseBinding Gesture="LeftDoubleCLick" Command="{StaticResource cmdEditTextblock}"/>
    </TextBlock.InputBindings>
</TextBlock>

Hinzufügen des richtigen RoutedCommand in UserControl.Resources

<UserControl.Resources>
    <RoutedCommand x:Key="cmdEditTextblock"/>
</UserControl.Resources>

und eine CommandBinding in UserControl.CommandBindings

<UserControl.CommandBindings>
    <CommandBinding Command="{StaticResource cmdEditTextblock}"
                    Executed="CmdEditTextblock_Executed"/>
</UserControl.CommandBindings>

Auch im Code hinter der Datei:

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

wird ersetzt durch

private void CmdEditTextblock_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        var grid = ((Grid)((TextBlock)e.OriginalSource).Parent);
        var tbx = (TextBox)grid.Children[1];
        ((TextBlock)e.OriginalSource).Visibility = Visibility.Collapsed;
        tbx.Visibility = Visibility.Visible;
        this.Dispatcher.BeginInvoke((Action)(() => Keyboard.Focus(tbx)), DispatcherPriority.Render);
    }

Für den Fall, dass einige Leute einen Doppelklick mit der linken Maustaste als Eingabegeste verwenden möchten, wie ich es getan habe...

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X