Reputation: 6368
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
Reputation: 6368
I have solved my problem. I got the currently editing textbox from e.OriginalSource.
Upvotes: 1
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
Reputation: 1668
In binding you can use UpdateSourceTrigger=PropertyChanged
so that in DataGrid_PreviewKeyDown
you 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