Получить текст TextBox, который находится внутри DataGridTemplateColumn в DataGrid

У меня есть 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>

Требования:

Получите текст внутри TextBlock, который находится внутри CellTemplate, универсальным способом.

Что я пробовал:

Когда я нажимаю Enter в ячейке TemplateColumn, я хочу, чтобы текст находился внутри TextBlock. Итак, я использовал событие PreviewKeyDown DataGrid следующим образом:

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 равно null.


person Vishal    schedule 13.07.2014    source источник
comment
Вы уже привязываетесь к FirstName, так почему бы не получить доступ к DataContext и просто использовать свойство FirstName?   -  person d.moncada    schedule 13.07.2014
comment
stackoverflow.com/questions/16997951/   -  person Sajeetharan    schedule 13.07.2014
comment
@d.moncada Извините за поздний ответ из-за отключения электричества. Как только я ввожу данные в ячейку и нажимаю ввод, мне нужно их значение. В то время свойство FirstName имеет значение null, поэтому я не могу его использовать.   -  person Vishal    schedule 14.07.2014
comment
@Sajeetharan Извините за поздний ответ, так как произошел сбой электричества. Я посетил страницу, которую вы предложили. Там ответчик использовал метод FindChild, который ожидает параметр имени, но я разрабатываю что-то вроде повторно используемого элемента управления, поэтому разработчик, который будет использовать этот элемент управления, может не предоставить имя, или я не смогу угадать имя элемента управления. Итак, я хочу общий подход.   -  person Vishal    schedule 14.07.2014
comment
Также в приведенном выше коде, который я предоставил в вопросе, я получаю ContentPresenter вместо Textblock в этой строке: var CellTemplate = ((DataGridCell)dep).Content;   -  person Vishal    schedule 14.07.2014
comment
Пожалуйста, опишите, какова ваша реальная цель, то есть что вы будете делать после того, как у вас будет текстовый блок. В большинстве случаев есть хорошее решение, которое включает механизмы привязки данных вместо использования событий формы.   -  person Mike Fuchs    schedule 16.07.2014
comment
@adabyron я разрабатываю собственную сетку данных. На самом деле я хочу проверить, содержит ли ячейка (которая в данный момент редактируется непосредственно перед нажатием клавиши ввода) какое-то значение или нет. Если он содержит какое-то значение, то фокус должен перейти к следующей ячейке, иначе он должен перейти к следующему элементу управления.   -  person Vishal    schedule 16.07.2014


Ответы (3)


В привязке вы можете использовать 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 как нулевое.

    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
            }
        }
    }
person Nitin Joshi    schedule 16.07.2014

Некоторые комментарии указывают на то, что доступ к ViewModel может быть здесь вариантом, хотя в некоторых случаях это будет более простой подход, он не обрабатывает поля, не связанные с данными, и, скорее всего, действительно будет менее общим.

Что мы хотим сделать, так это найти первого TextBlock ребенка, идущего по VisualTree щелкнутого DataGridCell. Рассмотрим следующий образец:

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

Для получения дополнительной информации и понимания того, как искать детей, посетите эту страницу, объясняющую разницу между визуальное дерево и логическое дерево в WPF.

person Bas    schedule 16.07.2014

Я решил свою проблему. Я получил текущее редактируемое текстовое поле от e.OriginalSource.

person Vishal    schedule 16.07.2014
comment
не могли бы вы помочь мне, как вы это сделали? - person RackM; 13.04.2018
comment
@C.jacking Это было давно, но я думаю, вы можете проверить значение e.OriginalSource. Там вы получите некоторое свойство, в котором доступен текст вашего текстового поля. - person Vishal; 13.04.2018
comment
Я использую Textblock, это сработает? Я сталкиваюсь с точно такой же проблемой - person RackM; 13.04.2018
comment
@ C.jacking Да, ты можешь попробовать. Но я покинул WPF до 2 лет, поэтому я этого не помню. - person Vishal; 13.04.2018