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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28168645/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 12:53:05  来源:igfitidea点击:

Automatic editing of WPF datagrid content when datagrid-cell gets focus

c#wpfxamldatagrid

提问by nullxff

I have a datagrid in WPF with a DataGridTextColumand a DataGridTemplateColum.

我在 WPF 中有一个数据网格,带有一个DataGridTextColum和一个DataGridTemplateColum

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

The CellTemplateSelectors return a DataTemplate with a TextBlock for the the Celltemplate resp. a TextBox for CellEditing!

CellTemplateSelectors 返回一个 DataTemplate,其中包含 Celltemplate 的 TextBlock。用于 CellEditing 的 TextBox!

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

Now I want to automatically Focus the TextBox when the DataGridCell gets the focus. The user should be able to edit the TextBox content without doubleclicking the cell.

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

I found this article:

我找到了这篇文章:

DataGrid Tips & Tricks: Single-Click Editingwhere I can get the Current DataGridCell, but how can I access the content to give the Textbox the focus to edit the content?

DataGrid 提示和技巧:单击编辑,我可以在其中获取 Current DataGridCell,但如何访问内容以将焦点放在文本框上以编辑内容?

This is my style:

这是我的风格:

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

This is my event handler:

这是我的事件处理程序:

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
}

How can I get the textbox with that event handler?

如何使用该事件处理程序获取文本框?

采纳答案by nullxff

I managed it, not the best solution but it works... When Cell gets focus I set it to editing mode.

我管理了它,不是最好的解决方案,但它有效......当 Cell 获得焦点时,我将其设置为编辑模式。

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

On Keydown I search for the visual child and give the focus.

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

Find Visual Child!

寻找视觉儿童!

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

回答by eran otzap

This seems to work :

这似乎有效:

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

回答by vesan

This approach works for me. It uses the fact that the DataGridwill always create a new instance of the template when the editing starts:

这种方法对我有用。它使用这样一个事实,即DataGrid在编辑开始时将始终创建模板的新实例:

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

and in the code behind:

并在后面的代码中:

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

As an added bonus, it also selects all text in the cell. It should work no matter how you enter the editing mode (double click, single click, pressing F2)

作为一个额外的好处,它还会选择单元格中的所有文本。无论您如何进入编辑模式(双击,单击,按 F2)它都应该工作

回答by Hisham

the simple answer for that create new control derived from datagrid control

创建从数据网格控件派生的新控件的简单答案

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

   }

回答by knilch

Hisham'ssuggestion works perfectly for me, but I would use OnCurrentCellChangedinstead, since OnSelectedCellsChangedwill not work when the SelectionUnitis CellOrRowHeader. In the latter case BeginEdit()would only be triggered when selection moves to a cell in another row. Stepping left or right will not trigger the event at all.

希沙姆的建议完全适用于我,但我会用OnCurrentCellChanged代替,因为OnSelectedCellsChanged当将无法正常工作SelectionUnitCellOrRowHeader。在后一种情况下BeginEdit(),只有在选择移动到另一行的单元格时才会触发。向左或向右步进根本不会触发事件。

Also, it is probably advisable to add a DependencyProperty to the custom control and check against it before triggering BeginEdit(), to prevent this behavior if so desired (as done by other DataGrids, such as XCeed). But this is not a critic - just something that I usually do.

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

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