使用数据模板(WPF)在ListBox中内联编辑TextBlock

使用WPF,我有一个ListBox控件,里面有一个DataTemplate 。 相关的XAML代码如下所示:

               

我想要做的是让TextBlock响应(双)单击,将其转换为TextBox 。 然后,用户可以编辑描述,然后按返回或更改焦点进行更改。

我尝试在与TextBlock相同的位置添加一个TextBox元素并使其可见性Collapsed ,但我不知道当用户单击TextBlock时如何导航到正确的TextBox 。 也就是说,我知道用户点击了某个TextBlock ,现在我显示哪个 TextBox

任何帮助将非常感激,

-Ko9

我在这些情况下所做的是使用XAML层次结构来确定要显示/隐藏的元素。 有点像:

     

用代码:

 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 ,我可以添加额外的error handling,并保证Grid只包含两个项目,并且它们的顺序永远不会改变。

编辑:此外,将其转换为UserControl允许您为每个实例创建一个Text属性,因此您可以命名每个实例并直接引用文本而无需通过((TextBox)myGrid.Children[1]).Text当前值((TextBox)myGrid.Children[1]).Text铸造。 这将使您的代码更加高效和干净。 如果将其设置为UserControl,则还可以命名TextBlockTextBox元素,因此根本不需要转换。

请参阅Nathan Wheeler的代码片段,以下代码是我昨天编写的完整UserControl源代码。 特别是,解决了绑定问题。 Nathan的代码很容易理解,但需要一些帮助才能使用数据绑定文本。

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

       

最后,您可以在XAML中使用此控件,如下所示:

  

请注意, Mode = TwoWay,UpdateSourceTrigger = PropertyChanged已设置。 它可以更改每种类型的绑定值。

执行此操作的理想方法是创建ClickEditableTextBlock控件,该控件默认呈现为TextBlock,但在用户单击时显示TextBox。 因为任何给定的ClickEditableTextBlock只有一个TextBlock和一个TextBox,所以没有匹配的问题。 然后在DataTemplate中使用ClickEditableTextBlock而不是单独的TextBlocks和TextBoxes。

这有将封装function封装在控件中的附带好处,因此您不会因编辑行为而污染主窗口代码隐藏,而且您可以轻松地在其他模板中重用它。


如果这听起来太费劲,您可以使用Tag或附加属性将每个TextBlock与TextBox相关联:

       

请注意在Tag上使用{Binding ElementName=tb}来引用名为tb的TextBox。

在您的代码隐藏中:

 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绑定,但为了简洁起见,我没有显示它。)

如果我可以补充,为了覆盖原始问题的(双)部分,在Youngjae的回复中,您在xaml文件中进行以下替换:

  

被替换为

      

在UserControl.Resources中添加正确的RoutedCommand

    

和UserControl.CommandBindings中的CommandBinding

    

同样在代码隐藏文件中:

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

如果有人想要将双击作为输入手势,就像我做的那样……