【问题标题】:Get Text of TextBox that resides inside DataGridTemplateColumn in DataGrid获取位于 DataGrid 中 DataGridTemplateColumn 内的 TextBox 的文本
【发布时间】:2014-07-13 15:47:29
【问题描述】:

我有一个包含一些列的 DataGrid。其中之一是模板列。该 TemplateColumn 声明如下:

<DataGridTemplateColumn Header="First Name">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding FirstName}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <TextBox Text="{Binding FirstName}" Loaded="TextBox_Loaded_1"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

要求:

以通用方式获取位于 CellTemplate 内的 TextBlock 内的文本。

我尝试过的:

当我在 TemplateColumn 的单元格上按 Enter 时,我想要 TextBlock 中的文本。所以,我使用了 DataGrid 的 PreviewKeyDown 事件如下:

private void DataGrid_PreviewKeyDown(.............)
{
    If(e.Key == Key.Enter)
    {
        DependencyObject dep = (DependencyObject)e.OriginalSource;

        if(dep != null && dep is DataGridCell)
        {
            var CellTemplate = ((DataGridCell)dep).Content; //gives me ContentPresenter instead of Textblock

            if (CellTemplate is TextBlock)
            {
                if (((TextBlock)CellTemplate).Text.Trim() == "")
                {
                    //Do whatever I want
                }
            }
        }
    }
}

上述代码返回一个 ContentPresenter 而不是 TextBlock。为什么会这样?

另外,ContentPresenter 的内容为空。

【问题讨论】:

  • 您已经绑定到 FirstName,为什么不访问 DataContext 并只使用 FirstName 属性?
  • @d.moncada 因停电,回复晚了。只要我在单元格中输入一些数据并按回车,我就想要它的值。当时 FirstName 属性为空,所以我不能使用它。
  • @Sajeetharan 抱歉,由于电力故障,回复晚了。我访问了您建议的页面。那里的回答者使用了 FindChild 方法,该方法需要 name 参数,但我正在开发类似可重用控件的东西,因此将使用此控件的开发人员可能不会提供名称,或者我可能无法猜测控件的名称。所以,我想要一个通用的方法。
  • 同样在我在问题中提供的上述代码中,在这一行中给了我 ContentPresenter 而不是 Textblock:var CellTemplate = ((DataGridCell)dep).Content;

标签: c# wpf datagrid


【解决方案1】:

在绑定中你可以使用UpdateSourceTrigger=PropertyChanged,这样在DataGrid_PreviewKeyDown你就不会发现FirstName属性为空。

<DataGridTemplateColumn Header="First Name">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding FirstName}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <TextBox Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" Loaded="TextBox_Loaded_1"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

DataGrid_PreviewKeyDown 事件中,您可以按如下方式获取行数据项,这次您不会将 Name 属性设为 null。

    private void DataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            var cell = e.OriginalSource as DataGridCell;
            if (cell != null)
            {
                var dataitem = cell.DataContext;  //Here you can you AS keyword to convert the DataContext to your item type.
                //dataitem.FirstName
            }
        }
    }

【讨论】:

    【解决方案2】:

    一些 cmets 表示访问 ViewModel 可能是这里的一个选项,虽然在某些情况下这会是更简单的方法,但它不处理非数据绑定字段并且很可能确实不太通用。

    我们要做的是找到第一个在被点击的DataGridCellVisualTree 上行走的TextBlock 孩子。考虑以下示例:

    <DataGrid Name="Test" PreviewKeyDown="DataGrid_PreviewKeyDown">
        <DataGrid.Columns>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="Bla Bla 123" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
    

    在后面的代码中:

    private void DataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            var pressedCell = e.OriginalSource as DataGridCell;
            if (pressedCell != null)
            {
                var textBlock = FindVisualChild<TextBlock>(pressedCell);
                if (textBlock != null)
                {
                    MessageBox.Show("Text: " + textBlock.Text); 
                    //or more useful stuff
                }
            }
        }
    }
    

    神奇之处在于FindVisualChild 方法(下面的实现)。该方法遍历所有子项,直到在深度优先搜索中找到第一个文本框出现。另一个好处是这也适用于标准自动生成的列!

    private static T FindVisualChild<T>(DependencyObject item)
        where T : DependencyObject
    {
        var childCount = VisualTreeHelper.GetChildrenCount(item);
        var result = item as T;
        //the for-loop contains a null check; we stop when we find the result. 
        //so the stop condition for this method is embedded in the initialization
        //of the result variable.
        for (int i = 0; i < childCount && result == null; i++)
        {
            result = FindVisualChild<T>(VisualTreeHelper.GetChild(item, i));
        }
        return result;
    }
    

    更多关于如何搜索儿童的信息和理解请看this page,解释WPF中可视树和逻辑树的区别。

    【讨论】:

      【解决方案3】:

      我已经解决了我的问题。我从 e.OriginalSource 获得了当前正在编辑的文本框。

      【讨论】:

      • 你能帮我看看你是怎么做到的吗?
      • @C.jacking 这是很久以前的事了,但我想你可以检查一下 e.OriginalSource 的值。在那里,您将获得一些属性,其中您的文本框的文本可用。
      • 我正在使用 Textblock 可以吗?我面临完全相同的问题
      • @C.jacking 是的,你可以试试。但是我在两年前就离开了WPF,所以我不记得了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-24
      • 1970-01-01
      • 1970-01-01
      • 2014-06-12
      • 2016-07-17
      • 1970-01-01
      相关资源
      最近更新 更多