Vishal
Vishal

Reputation: 6368

Get Text of TextBox that resides inside DataGridTemplateColumn in DataGrid

I have a DataGrid with some columns. One of them is a Template Column. That TemplateColumn is declared as shown below :

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

Requirements :

Get the text inside the TextBlock that resides inside CellTemplate in a generic way.

What I have tried :

When I press Enter on TemplateColumn's cell, I want the Text inside TextBlock. So, I have used the PreviewKeyDown Event of DataGrid as follows :

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

The code described above returns a ContentPresenter instead of TextBlock. Why this happens?

Also, Content of the ContentPresenter is null.

Upvotes: 0

Views: 2193

Answers (3)

Vishal
Vishal

Reputation: 6368

I have solved my problem. I got the currently editing textbox from e.OriginalSource.

Upvotes: 1

Bas
Bas

Reputation: 27085

Some comments indicate that accessing a ViewModel might be an option here, while this would be the easier approach in some cases, it doesn't handle non-databound fields and is most likely going to be less generic indeed.

What we want to do is find the first TextBlock child walking down the VisualTree of the clicked DataGridCell. Consider the following sample:

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

In the code behind:

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

The magic lies in the FindVisualChild method (implementation below). The method walks down all children until it finds the first textbox occurrance in a depth first search. Added benefit is that this works for standard auto-generated columns as well!

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

For more information and understanding about how to search for children please take a look at this page, explaining the difference between the visual tree and the logical tree in WPF.

Upvotes: 1

Nitin Joshi
Nitin Joshi

Reputation: 1668

In binding you can use UpdateSourceTrigger=PropertyChanged so that in DataGrid_PreviewKeyDownyou wont find FirstName property as null.

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

And in DataGrid_PreviewKeyDown event you can get your row data item as follows and this time you won't get Name property as 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
            }
        }
    }

Upvotes: 1

Related Questions