0

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.

Vishal
  • 6,238
  • 10
  • 82
  • 158
  • 2
    You're binding to FirstName already, so why not access the DataContext and just use the FirstName property? – d.moncada Jul 13 '14 at 15:54
  • http://stackoverflow.com/questions/16997951/how-to-access-datagrid-template-column-textbox-text-wpf-c-sharp – Sajeetharan Jul 13 '14 at 16:02
  • @d.moncada Sorry for the late reply due to electricity failure. As soon as I enter some data in a cell and press enter I want its value. At that time FirstName property is null, so I can't use that. – Vishal Jul 13 '14 at 21:49
  • @Sajeetharan Sorry for the late reply as there was an electricity failure. I have visited the page that you suggested. There the answerer used the FindChild method which expects the name parameter but I am developing something like re-usable control, so the developer who will use this control may not provide a name or I may not be able to guess the name of the control. So, I want a generic approach. – Vishal Jul 13 '14 at 21:52
  • Also in the above code that I provided in the question gives me ContentPresenter instead of Textblock in this line : `var CellTemplate = ((DataGridCell)dep).Content;` – Vishal Jul 13 '14 at 21:54
  • Please describe what your actual goal is, i.e. what you will do after you have the textblock content. Most times there is a good solution that involves the databinding mechanisms instead of using form events. – Mike Fuchs Jul 16 '14 at 10:04
  • @adabyron i am developing a custom datagrid. Actually i want to check if the cell (that is currently edited just before the enter key was pressed) contains some value or not. If it contains some value then the focus should proceed to the next cell else it should proceed to the next control. – Vishal Jul 16 '14 at 10:18

3 Answers3

1

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
            }
        }
    }
Nitin Joshi
  • 1,638
  • 1
  • 14
  • 17
1

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.

Bas
  • 26,772
  • 8
  • 53
  • 86
1

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

Vishal
  • 6,238
  • 10
  • 82
  • 158