WPF数据表格单元格获得焦点时自动编辑内容

15

我在WPF中有一个数据网格,其中包含一个DataGridTextColumn和一个DataGridTemplateColumn

<DataGridTextColumn Width="4*" IsReadOnly="True" x:Name="dataGridColumnDescription" 
Header="Description" Binding="{Binding Description}">
</DataGridTextColumn>

<DataGridTemplateColumn CellStyle="{StaticResource CellEditing}" IsReadOnly="False" Width="*" Header="Value" 
CellEditingTemplateSelector="{StaticResource myCellEditingTemplateSelectorValue}" 
CellTemplateSelector="{StaticResource myCellTemplateSelectorValue}">
</DataGridTemplateColumn>

CellTemplateSelectors返回一个带有TextBlock的DataTemplate,用于单元格模板,或者一个带有TextBox的DataTemplate,用于单元格编辑!

<DataTemplate x:Key="dGridStringValueTemplate">
    <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Path=Value}"/>
</DataTemplate>

<DataTemplate x:Key="dGridStringValueTemplateEditing">
    <TextBox TextAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" BorderThickness="1" Text="{Binding Path=Value, UpdateSourceTrigger=LostFocus}"/>
</DataTemplate>

现在我想要在DataGridCell获得焦点时自动聚焦TextBox。用户应该能够在不双击单元格的情况下编辑TextBox内容。

我找到了这篇文章:

DataGrid Tips & Tricks: Single-Click Editing,在那里我可以获取当前的DataGridCell,但是如何访问内容以使TextBox获得焦点并编辑内容呢?

这是我的样式:

<Style x:Key="CellEditing" TargetType="{x:Type DataGridCell}">
    <EventSetter Event="PreviewMouseLeftButtonDown" Handler="myDataGridMain_PreviewMouseLeftButtonDown"></EventSetter>
</Style>

这是我的事件处理程序:

private void myDataGridMain_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    DataGridCell cell = sender as DataGridCell;     // cell ist not null

    DataGridTemplateColumn col = cell.Column as DataGridTemplateColumn; //col is not null

    DataTemplate template = col.CellTemplate;  //this is null
}

我怎样才能得到带有该事件处理程序的文本框?


当您希望网格始终可编辑时,为什么需要编辑模板?只需添加带有文本框的单元格模板,并更改文本框本身的样式即可,这样您就不必编写聚焦代码。 - WPFKK
@WPFKK 这样做不起作用,因为数据网格不会切换到“编辑模式”,因此输入值不会绑定到源。 - Rémy Esmery
5个回答

20

这似乎有效:

    <DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellEditingTemplate>
                    <DataTemplate>
                        <TextBox  FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"></TextBox>
                    </DataTemplate>
                </DataGridTemplateColumn.CellEditingTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>

谢谢你的回答。与模板选择器结合使用,这无法工作 :( - nullxff
1
谢谢,你是伟大的巫师! - Yola
1
我有一个带有组合框和文本框的复杂数据网格。这个示例解决了我所有单元格类型的问题。 - Hamed Rezaei
@HamedR 很棒 :) - eran otzap
我正在使用模板选择器,上述方法对我有效。 - Drew Noakes
@HamedRezaei 很有趣。当我将焦点管理器添加到我的XAML时,每个带有文本框的列都会显示文本框聚焦,我仍然需要按两次Tab键。 - Mike

5

这个方法对我很有效。它利用了 DataGrid 在编辑开始时总是会创建一个新的模板实例的事实:

<DataGridTemplateColumn.CellEditingTemplate>
    <DataTemplate>
        <TextBox Text="{Binding MyProperty}" 
                 Loaded="TextBox_Loaded"></TextBox>
    </DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>

在代码后台:

private void TextBox_Loaded(object sender, RoutedEventArgs e)
{
    ((TextBox)sender).Focus();
    ((TextBox)sender).SelectAll();
}

作为额外的奖励,它还会选择单元格中的所有文本。无论您如何进入编辑模式(双击、单击或按下F2键),它都应该有效。

2

我处理了它,虽不是最佳解决方案,但可以正常工作... 当单元格得到焦点时,我将其设置为编辑模式。

private void myDataGridMain_OnFocus(object sender, RoutedEventArgs e)
{
    DataGridCell cell = sender as DataGridCell;
    if (cell != null)
        cell.IsEditing = true;
    //var test = FindVisualChild<TextBlock>(cell);
}

在 Keydown 事件中,我会寻找视觉子元素并将焦点放在其上。
private void myDataGridMain_KeyDown(object sender, KeyEventArgs e)
        {
            DataGridCell cell = sender as DataGridCell;

            if (e.Key == Key.Enter)
            {   //give cell the focus
                cell.Focus();
            }
            else
            {
                if ((cell != null))
                {
                    TextBox textbox = FindVisualChild<TextBox>(cell);
                    if (textbox != null)
                    {   //TextBox has benn found
                        if ((textbox as TextBox).IsFocused == false)
                        {
                            (textbox as TextBox).SelectAll();
                        }
                        (textbox as TextBox).Focus();
                    }

                    CheckBox chkbox = FindVisualChild<CheckBox>(cell);
                    if (chkbox != null)
                    {   //Checkbox has been found
                        (chkbox as CheckBox).Focus();
                    }

                    ComboBox combbox = FindVisualChild<ComboBox>(cell);
                    if (combbox != null)
                    {   //ComboBox has been found
                        (combbox as ComboBox).Focus();
                    }
                }
            }
        }

找到可视化子元素!
public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        if (child != null && child is T)
            return (T)child;
        else
        {
            T childOfChild = FindVisualChild<T>(child);
            if (childOfChild != null)
                return childOfChild;
        }
    }
    return null;
}

你是如何为每个单元格订阅OnFocus事件的? - Mike

0

简单的答案是创建一个从DataGrid控件派生的新控件。

  using System.Windows.Controls;

   public class CustomDataGrid : DataGrid
   {

    protected override void OnSelectedCellsChanged(SelectedCellsChangedEventArgs e)
    {
        //to make sure cell is selected
        var cells = e.AddedCells.FirstOrDefault();
        if (cells != null)
        {
            this.BeginEdit();

        }
        base.OnSelectedCellsChanged(e);
    }

   }

0

Hisham's 的建议对我来说完美地解决了问题,但我会使用 OnCurrentCellChanged,因为当 SelectionUnitCellOrRowHeader 时,OnSelectedCellsChanged 将无法工作。在后一种情况下,只有当选择移动到另一行的单元格时,BeginEdit() 才会被触发。向左或向右移动将根本不会触发事件。

此外,可能建议在自定义控件中添加一个 DependencyProperty,并在触发 BeginEdit() 之前检查它,以防止这种行为(如 XCeed 等其他 DataGrids 所做的那样)。但这并不是批评 - 只是我通常做的事情。

    protected override void OnCurrentCellChanged(EventArgs e)
    {
        // Make sure a cell is selected and only enter edit mode
        // if this is the desired behavior 
        if (CurrentCell != null && EditTrigger == EditTriggers.CellsCurrent)
        {
            this.BeginEdit();
        }
        base.OnCurrentCellChanged(e);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接