簡體   English   中英

使用數據模板 (WPF) 在 ListBox 中內聯編輯 TextBlock

[英]Inline editing TextBlock in a ListBox with Data Template (WPF)

使用 WPF,我有一個ListBox控件,里面有一個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響應(雙擊)將其變成TextBox 然后,用戶可以編輯描述,然后按回車鍵或更改焦點進行更改。

我嘗試在與 TextBlock 相同的位置添加一個TextBox元素並使其可見性Collapsed ,但是當用戶單擊TextBlock時,我不知道如何導航到正確的TextBox 也就是說,我知道用戶點擊了某個TextBlock ,現在我要顯示哪個TextBox

任何幫助將不勝感激,

-Ko9

我在這些情況下所做的是使用 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將只包含兩個項目,並且它們的順序永遠不會改變。

編輯:此外,將其轉換為 UserControl 允許您為每個實例創建一個Text屬性,因此您可以為每個實例命名並直接引用文本,而無需通過((TextBox)myGrid.Children[1]).Text獲取當前值((TextBox)myGrid.Children[1]).Text轉換。 這將使您的代碼更加高效和干凈。 如果你把它變成一個用戶控件,你也可以命名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)
        {
            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>

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

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

請注意,設置了Mode=TwoWay、UpdateSourceTrigger=PropertyChanged 它可以更改每種類型的綁定值。

理想的方法是創建一個ClickEditableTextBlock控件,該控件默認呈現為 TextBlock,但在用戶單擊它時顯示一個 TextBox。 因為任何給定的 ClickEditableTextBlock 只有一個 TextBlock 和一個 TextBox,所以您不會遇到匹配問題。 然后在 DataTemplate 中使用 ClickEditableTextBlock 而不是單獨的 TextBlocks 和 TextBoxes。

這具有將功能封裝在控件中的附帶好處,因此您不會因編輯行為污染主窗口代碼隱藏,而且您可以輕松地在其他模板中重用它。


如果這聽起來太費力,您可以使用 Tag 或附加屬性將每個 TextBlock 與 TextBox 關聯:

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

請注意在 Tag 上使用{Binding ElementName=tb}來引用名為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 綁定,但為簡潔起見,我沒有顯示。)

如果我可以補充,為了涵蓋原始問題的(雙重)部分,在 Youngjae 的回復中,您在 xaml 文件中進行以下替換:

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

被替換為

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

在 UserControl.Resources 中添加正確的 RoutedCommand

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

和 UserControl.CommandBindings 中的 CommandBinding

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

同樣在文件后面的代碼中:

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

萬一有些人想左鍵雙擊作為輸入手勢,就像我一樣。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM