Accessing and Mondifying DataTemplate Element Runtime for CellTemplate
Dear all,
I have created one Datatemplate which i am assigning to Celltemplate of a gridviewCollumn.
Can i access the elements within DataTemplate at runtime in Code ?
If yes then how ?
Thanks !
Answers
- I've quickly mocked up a helper class which can do the trick:
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
namespace Sheva.Windows.Component
{
public static class ListViewHelper
{
public static FrameworkElement GetElementFromCellTemplate(ListView listView, Int32 column, Int32 row, String name)
{
if (row >= listView.Items.Count || row < 0)
{
throw new ArgumentOutOfRangeException("row");
}
GridView gridView = listView.View as GridView;
if (gridView == null)
{
return null;
}
if (column >= gridView.Columns.Count || column < 0)
{
throw new ArgumentOutOfRangeException("column");
}
ListViewItem item = listView.ItemContainerGenerator.ContainerFromItem(listView.Items[row]) as ListViewItem;
if (item != null)
{
GridViewRowPresenter rowPresenter = GetFrameworkElementByName<GridViewRowPresenter>(item);
if (rowPresenter != null)
{
ContentPresenter templatedParent = VisualTreeHelper.GetChild(rowPresenter, column) as ContentPresenter;
DataTemplate dataTemplate = gridView.Columns[column].CellTemplate;
if (dataTemplate != null && templatedParent != null)
{
return dataTemplate.FindName(name, templatedParent) as FrameworkElement;
}
}
}
return null;
}
private static T GetFrameworkElementByName<T>(FrameworkElement referenceElement) where T : FrameworkElement
{
FrameworkElement child = null;
for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceElement); i++)
{
child = VisualTreeHelper.GetChild(referenceElement, i) as FrameworkElement;
System.Diagnostics.Debug.WriteLine(child);
if (child != null && child.GetType() == typeof(T))
{
break;
}
else if (child != null)
{
child = GetFrameworkElementByName<T>(child);
if (child != null && child.GetType() == typeof(T))
{
break;
}
}
}
return child as T;
}
}
}
Hope this helps Dear Marco,
Finally, I got the solution.
Your code helped me a lot to solve this problem. Only for certain senarios i have to change some code at some places.
like . control.ApplyTemplate() method i had to call.
Thanks yaar !!
I am happy now.
Regards,
-Manish.
Find below the updated function.
public static FrameworkElement GetElementFromCellTemplate(ListView listView, Int32 column, Int32 row, String name){
if (row >= listView.Items.Count || row < 0){
throw new ArgumentOutOfRangeException("row");}
GridView gridView = listView.View as GridView; if (gridView == null){
return null;}
if (column >= gridView.Columns.Count || column < 0){
throw new ArgumentOutOfRangeException("column");}
ListViewItem item = listView.ItemContainerGenerator.ContainerFromItem(listView.Items[row]) as ListViewItem; if (item != null){
if (!item.IsLoaded){
item.ApplyTemplate();
}
GridViewRowPresenter rowPresenter = GetFrameworkElementByName<GridViewRowPresenter>(item); if (rowPresenter != null){
rowPresenter.ApplyTemplate();
ContentPresenter templatedParent = VisualTreeHelper.GetChild(rowPresenter, column) as ContentPresenter; DataTemplate dataTemplate = gridView.Columns[column].CellTemplate; if (dataTemplate != null && templatedParent != null){
templatedParent.ApplyTemplate();
return dataTemplate.FindName(name, templatedParent) as FrameworkElement;}
}
}
return null;}
- Marked As Answer byMann._ Monday, July 14, 2008 6:16 AM
- Here is a version in VB that seems to work. It includes the ApplyTemplate mods.
Public NotInheritable Class ListViewHelper Public Shared Function GetElementFromCellTemplate(ByVal lv As ListView, _ ByVal column As Integer, _ ByVal row As Integer, _ ByVal name As String) As FrameworkElement If row >= lv.Items.Count Or row < 0 Then Throw New ArgumentOutOfRangeException("row") End If Dim gv As GridView = lv.View If gv Is Nothing Then Return Nothing End If If column >= gv.Columns.Count Or column < 0 Then Throw New ArgumentOutOfRangeException("column") End If Dim item As ListViewItem = lv.ItemContainerGenerator.ContainerFromItem(lv.Items(row)) If item IsNot Nothing Then If Not item.IsLoaded Then item.ApplyTemplate() End If Dim gvrp As GridViewRowPresenter = GetFrameworkElementByName(Of GridViewRowPresenter)(item) If gvrp IsNot Nothing Then gvrp.ApplyTemplate() Dim cp As ContentPresenter = VisualTreeHelper.GetChild(gvrp, column) Dim dt As DataTemplate = gv.Columns(column).CellTemplate If dt IsNot Nothing And cp IsNot Nothing Then cp.ApplyTemplate() Return dt.FindName(name, cp) Else Return Nothing End If Else Return Nothing End If Else Return Nothing End If End Function Public Shared Function GetFrameworkElementByName(Of T As FrameworkElement)(ByVal fe As FrameworkElement) As T Dim child As FrameworkElement = Nothing For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(fe) - 1 child = DirectCast(VisualTreeHelper.GetChild(fe, i), FrameworkElement) 'System.Diagnostics.Debug.WriteLine(child) If child IsNot Nothing And TypeOf child Is T Then Exit For ElseIf child IsNot Nothing Then child = GetFrameworkElementByName(Of T)(child) If child IsNot Nothing And TypeOf child Is T Then Exit For End If End If Next i Return DirectCast(child, T) End Function End Class
All Replies
- I've quickly mocked up a helper class which can do the trick:
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
namespace Sheva.Windows.Component
{
public static class ListViewHelper
{
public static FrameworkElement GetElementFromCellTemplate(ListView listView, Int32 column, Int32 row, String name)
{
if (row >= listView.Items.Count || row < 0)
{
throw new ArgumentOutOfRangeException("row");
}
GridView gridView = listView.View as GridView;
if (gridView == null)
{
return null;
}
if (column >= gridView.Columns.Count || column < 0)
{
throw new ArgumentOutOfRangeException("column");
}
ListViewItem item = listView.ItemContainerGenerator.ContainerFromItem(listView.Items[row]) as ListViewItem;
if (item != null)
{
GridViewRowPresenter rowPresenter = GetFrameworkElementByName<GridViewRowPresenter>(item);
if (rowPresenter != null)
{
ContentPresenter templatedParent = VisualTreeHelper.GetChild(rowPresenter, column) as ContentPresenter;
DataTemplate dataTemplate = gridView.Columns[column].CellTemplate;
if (dataTemplate != null && templatedParent != null)
{
return dataTemplate.FindName(name, templatedParent) as FrameworkElement;
}
}
}
return null;
}
private static T GetFrameworkElementByName<T>(FrameworkElement referenceElement) where T : FrameworkElement
{
FrameworkElement child = null;
for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceElement); i++)
{
child = VisualTreeHelper.GetChild(referenceElement, i) as FrameworkElement;
System.Diagnostics.Debug.WriteLine(child);
if (child != null && child.GetType() == typeof(T))
{
break;
}
else if (child != null)
{
child = GetFrameworkElementByName<T>(child);
if (child != null && child.GetType() == typeof(T))
{
break;
}
}
}
return child as T;
}
}
}
Hope this helps Thank you sir ...
This is really fantastic !!
it worked for me..
Thank you verymuch . i was struggling a lot for this solution.
Thanks!
- Regards
Mann..
Hi Marco,
Above Code Worked fine for me. But in one sinario it's not satisfying my requirements.
I am assigning ItemSource for lstView at runtime. and then assigning CellTemplates to each newly created collumn.
Then i am trying to fetch a parent grid from DataTemplat's Element for each cell and assigning DataContext to it.(as per my business object).
Now actually this filling of gridview should happen when i change a combobox value at top of my window.
So, i am writing whole code in Combobox changed.
see below for the imp. statments..
lstDays.ItemsSource = LABRESULTS[currentDeptIndex].Investigations;
/* --code for adding Collumns in to ListView goes here ..
-----
-----
--*/
then Say for the first Cell in a gridview
Grid currentCellTemplateGridContainer = (Grid)ListViewHelper.GetElementFromCellTemplate(lstViewDays, 0, 0, "grdLabResultDayPresenter"); /* Where grdLabResultDayPresenter is a parent grid in CellTemplate */
From here GetElementFromCellTemplate() is called inside that at following statement it returns null in ListViewItem.
ListViewItem item = listView.ItemContainerGenerator.ContainerFromItem(listView.Items[row]) as ListViewItem;
I checked for listView.ItemContainerGenerator.Status . It returns not started..
Also i tried using listView.ItemContainerGenerator.ContainerFromIndex(0); it also gives the same result.
The whole idea works only when i write the code for filling listview cells somewhere else and not in a Combobox change event. but my requirement is to refresh the listview based on the value selected in combobox.
What can i do for this ?
Thanks !
- Manish.
- ListViewHelper.GetElementFromCellTemplate() will work correctly if all the containers/ListViewItems have been generated by the underlying ListView's item container generator, so if you want it to work properly for all scenarios, you need to turn off the UI virtualization for ListView, the following code shows how to turn off UI virtualization for ListView:
<ListView>
And when you dynamically populating the ListView as you did above, because at that time, the containers/ListViewItems has not been generated, you need to wait until the containers are generated, you can simply hook up to ListView.ItemsContainerGenerator.StatusChanged event, and inside this event, you need to check the ListView.ItemsContainerGenerator.Status property to see if it's set to GeneratorStatus.ContainersGenerated, if so, you can safely call ListViewHelper.GetElementFromCellTemplate() at that time.
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
Hope this helps Dear Marco,
I struggled a lot with this, but it's not geeting for the senario i mentioned (immediately when Combobox selection Change).
is there any other way to explicitely chage the status of ListView.ItemsContainerGenerator.Status property.
Because, it shows me 'Not Generated' for the senario i had written.
please, post the solution when you get it.
I am at the bottom of the solution now, only 0.01% far from proposed output.
Thnaks for ur help.
Regards ,
-Manish
Manish wrote:
is there any other way to explicitely chage the status of ListView.ItemsContainerGenerator.Status property.
As far as I know, there is no explicit way to change the status of item container generator, even if there is a method to do so, it's not a good practice to explicitly manipulate the item container generator.
The best bet you can do is hook up to the StatusChanged event and wait for the right time to access the named element within cell template as following code shows:lstViewDays.ItemContainerGenerator.StatusChanged += delegate
{
if (lstViewDays.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
Grid currentCellTemplateGridContainer = (Grid)ListViewHelper.GetElementFromCellTemplate(lstViewDays, 0, 0, "grdLabResultDayPresenter");
}
};
Hope this helpsHi Marco,
Now i am getting that ItemContainerGenerated status as Generated.
As i changed the sequence of statement in which i was assigning DataSource to ListView.
Now following line always returns null in rowPresenter for that senario.(Combobox changed)
GridViewRowPresenter rowPresenter = GetFrameworkElementByName<GridViewRowPresenter>(item);Here item is also getting initialised which before always was returning null. But GetFrameworkElementByName<GridViewRowPresenter>(item); returns Null..
Can you help me in this ? This is a final stage for solution.
Thanks !
-Manish
Manish wrote:
Here item is also getting initialised which before always was returning null. But GetFrameworkElementByName<GridViewRowPresenter>(item); returns Null..
If I understand you correctly, you mean the container/ListViewItem is initialized, but GetFrameworkElementByName() call will return null, if this is the case, you can insert ApplyTemplate() call before GetFrameworkElementByName() call:if (!item.IsLoaded)
{
item.ApplyTemplate();
}
GridViewRowPresenter rowPresenter = GetFrameworkElementByName<GridViewRowPresenter>(item);
Hope this helpsDear Marco,
Finally, I got the solution.
Your code helped me a lot to solve this problem. Only for certain senarios i have to change some code at some places.
like . control.ApplyTemplate() method i had to call.
Thanks yaar !!
I am happy now.
Regards,
-Manish.
Find below the updated function.
public static FrameworkElement GetElementFromCellTemplate(ListView listView, Int32 column, Int32 row, String name){
if (row >= listView.Items.Count || row < 0){
throw new ArgumentOutOfRangeException("row");}
GridView gridView = listView.View as GridView; if (gridView == null){
return null;}
if (column >= gridView.Columns.Count || column < 0){
throw new ArgumentOutOfRangeException("column");}
ListViewItem item = listView.ItemContainerGenerator.ContainerFromItem(listView.Items[row]) as ListViewItem; if (item != null){
if (!item.IsLoaded){
item.ApplyTemplate();
}
GridViewRowPresenter rowPresenter = GetFrameworkElementByName<GridViewRowPresenter>(item); if (rowPresenter != null){
rowPresenter.ApplyTemplate();
ContentPresenter templatedParent = VisualTreeHelper.GetChild(rowPresenter, column) as ContentPresenter; DataTemplate dataTemplate = gridView.Columns[column].CellTemplate; if (dataTemplate != null && templatedParent != null){
templatedParent.ApplyTemplate();
return dataTemplate.FindName(name, templatedParent) as FrameworkElement;}
}
}
return null;}
- Marked As Answer byMann._ Monday, July 14, 2008 6:16 AM
- Here is a version in VB that seems to work. It includes the ApplyTemplate mods.
Public NotInheritable Class ListViewHelper Public Shared Function GetElementFromCellTemplate(ByVal lv As ListView, _ ByVal column As Integer, _ ByVal row As Integer, _ ByVal name As String) As FrameworkElement If row >= lv.Items.Count Or row < 0 Then Throw New ArgumentOutOfRangeException("row") End If Dim gv As GridView = lv.View If gv Is Nothing Then Return Nothing End If If column >= gv.Columns.Count Or column < 0 Then Throw New ArgumentOutOfRangeException("column") End If Dim item As ListViewItem = lv.ItemContainerGenerator.ContainerFromItem(lv.Items(row)) If item IsNot Nothing Then If Not item.IsLoaded Then item.ApplyTemplate() End If Dim gvrp As GridViewRowPresenter = GetFrameworkElementByName(Of GridViewRowPresenter)(item) If gvrp IsNot Nothing Then gvrp.ApplyTemplate() Dim cp As ContentPresenter = VisualTreeHelper.GetChild(gvrp, column) Dim dt As DataTemplate = gv.Columns(column).CellTemplate If dt IsNot Nothing And cp IsNot Nothing Then cp.ApplyTemplate() Return dt.FindName(name, cp) Else Return Nothing End If Else Return Nothing End If Else Return Nothing End If End Function Public Shared Function GetFrameworkElementByName(Of T As FrameworkElement)(ByVal fe As FrameworkElement) As T Dim child As FrameworkElement = Nothing For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(fe) - 1 child = DirectCast(VisualTreeHelper.GetChild(fe, i), FrameworkElement) 'System.Diagnostics.Debug.WriteLine(child) If child IsNot Nothing And TypeOf child Is T Then Exit For ElseIf child IsNot Nothing Then child = GetFrameworkElementByName(Of T)(child) If child IsNot Nothing And TypeOf child Is T Then Exit For End If End If Next i Return DirectCast(child, T) End Function End Class - Hi,
I have a combobox in one of the column of grid. I have used CellTemplate for it. Combobox value for each row is different. I am able to bind the combobox properly but unable to retrieve the selected value of combobox. Below is the code I have used. If any solution is available it will be of great help.
Using System;
Using System.Windows;
Using Microsoft.Practices.CompositeUI;
Using Microsoft.Practices.CompositeUI.SmartParts;
Using Microsoft.Practices.CompositeUI.WPF;
Using Microsoft.Practices.ObjectBuilder;
Using System.Windows.Media;
Using System.Windows.Controls;
Using TreeListViewSample.Infrastructure.Interface;
Using System.Windows.Data;
Namespace TreeListViewSample.TreeListViewModule
{
/// <summary>
/// Interaction logic for TreeListView.xaml
/// </summary>
public partial class TreeListView : System.Windows.Controls.UserControl, ITreeListView, IDisposable
{
/// <summary>
/// Initialize a new instance of <see cref="TreeListView"/>.
/// <summary>
DataTemplate template;
FrameworkElementFactory factoryComboBox;
GridView myGridView;
public TreeListView()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(OnLoad);
}
/// <summary>
/// Called when WPF has loaded the control and is ready to display it.
/// <summary>
/// <param name="sender">Source of the event</param>
/// <param name="e">Extra information about the event</param>
public void OnLoad(object sender, RoutedEventArgs e)
{
_presenter.OnViewReady();
BindGrid();
}
private void listView1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
int idx=listView1.Items.IndexOf(listView1.SelectedItem);
ComboBox currentCellTemplateGridContainer = (ComboBox)ListViewHelper.GetElementFromCellTemplate(listView1, 3, 1, "ResponseAndRating"); /* Where
public static class ListViewHelper
{
public static FrameworkElement GetElementFromCellTemplate(ListView listView, Int32 column, Int32 row, String name)
{
if (row >= listView.Items.Count || row < 0)
{
throw new ArgumentOutOfRangeException("row");
}
GridView gridView = listView.View as GridView;
if (gridView == null)
{
return null;
}
if (column >= gridView.Columns.Count || column < 0)
{
throw new ArgumentOutOfRangeException("column");
}
ListViewItem item = listView.ItemContainerGenerator.ContainerFromItem(listView.Items[row]) as ListViewItem;
if (item != null)
{
if (!item.IsLoaded)
{
item.ApplyTemplate();
}
GridViewRowPresenter rowPresenter = GetFrameworkElementByName<GridViewRowPresenter>(item);
if (rowPresenter != null)
{
rowPresenter.ApplyTemplate();
ContentPresenter templatedParent = VisualTreeHelper.GetChild(rowPresenter, column) as ContentPresenter;
DataTemplate dataTemplate =gridView.Columns[column].CellTemplate;
if (dataTemplate != null && templatedParent != null)
{
templatedParent.ApplyTemplate();
return dataTemplate.FindName(name, templatedParent) as FrameworkElement;
}
}
}
return null;
}
private static T GetFrameworkElementByName<T>(FrameworkElement referenceElement) where T : FrameworkElement
{
FrameworkElement child = null;
for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceElement); i++)
{ child =
VisualTreeHelper.GetChild(referenceElement, i) as FrameworkElement;
System.Diagnostics.
Debug.WriteLine(child);
if (child != null && child.GetType() == typeof(T)) { break; }
else if (child != null) { child = GetFrameworkElementByName<T>(child);
if (child != null && child.GetType() == typeof(T)) { break; } } } return child as T; } }
private void BindGrid()
{
try
{
string[] arr1 = new string[10];
arr1[0] = "FirstItem";
arr1[1] = "SecondItem";
arr1[3] = "ThirdItem";
string[] arr2 = new string[10];
arr2[0] = "S2FirstItem";
arr2[1] = "S2SecondItem";
arr2[3] = "S2ThirdItem";
myGridView = new GridView();
myGridView.AllowsColumnReorder = true;
myGridView.ColumnHeaderToolTip = "Employee Information";
GridViewColumn gvc1 = new GridViewColumn();
gvc1.DisplayMemberBinding =
new Binding("FirstName");
gvc1.Header = "FirstName";
gvc1.Width = 100;
myGridView.Columns.Add(gvc1);
GridViewColumn gvc2 = new GridViewColumn();
gvc2.DisplayMemberBinding = new Binding("LastName");
gvc2.Header = "Last Name";
gvc2.Width = 100;
myGridView.Columns.Add(gvc2);
GridViewColumn gvc3 = new GridViewColumn();
gvc3.DisplayMemberBinding = new Binding("EmployeeNumber");
gvc3.Header = "Employee No.";
gvc3.Width = 100;
myGridView.Columns.Add(gvc3);
GridViewColumn gvc4 = new GridViewColumn();
gvc4.Header = "RatingAndResponse";
myGridView.Columns.Add(gvc4);
ComboBox cmb = new ComboBox();
cmb.Width = 60;
template =
new DataTemplate();
factoryComboBox = new FrameworkElementFactory(typeof(ComboBox));
factoryComboBox.SetValue(ComboBox.HorizontalAlignmentProperty, HorizontalAlignment.Right);
factoryComboBox.SetBinding(ComboBox.ItemsSourceProperty, new Binding("ResponseAndRating"));
factoryComboBox.SetValue(ComboBox.WidthProperty, cmb.Width);
template.VisualTree = factoryComboBox;
gvc4.CellTemplate = template;
System.Collections.ObjectModel.
Collection<Employee> emp = new System.Collections.ObjectModel.Collection<Employee>();
Employee employee = new Employee();
employee.FirstName = "Hr";
employee.LastName = "Dv";
employee.EmployeeNumber = "1";
employee.ResponseAndRating = arr1;
emp.Add(employee);
Employee employee1 = new Employee();
employee1.FirstName = "Bg";
employee1.LastName = "Dv";
employee1.EmployeeNumber = "2";
employee1.ResponseAndRating = arr2;
emp.Add(employee1);
listView1.ItemsSource = emp;
listView1.View = myGridView;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public class Employee
{
private string _firstName;
private string _lastName;
private string _empNumber;
private string[] _responseAndRating;
public string FirstName
{
get{return _firstName;} set{_firstName = value;} }
public string LastName
{
get { return _lastName; } set { _lastName=value;} }
public string EmployeeNumber
{
get { return _empNumber; } set { _empNumber=value;} }
public string[] ResponseAndRating
{
get { return _responseAndRating; }
set { _responseAndRating = value; }
}
}
}
- Hello,
This is a great code and it's helping us testing wpf scenarios. Still I'm facing a problem with
ListViewItem
item = listView.ItemContainerGenerator.ContainerFromItem( listView.Items[row] ) as ListViewItem;
I have a ListView bound to a DataView with rows in diferent DataRowStates. When I try to get the ListViewItem from a row that is on DataRowState = Modified on BeginEdit, ContainerFromItem returns null when the row is actually showing on the ListView.
Is there another API I could use or why is it not working?
Best Regards
Juan
This posting is provided "AS IS" with no warranties, and confers no rights. - Nevermind...
The problem was that I got null for rows that haven't been displayed yet.
At least what I did was making the ListView big enough to display all my rows, is there a better work around?
Thanks
Juan
This posting is provided "AS IS" with no warranties, and confers no rights. - Hello,
I use this code too and I get the same things a remarked bij Juan. I use VB so:
Dim
item As ListViewItem = TryCast(listView.ItemContainerGenerator.ContainerFromItem(listView.Items(row)), ListViewItem)
This returns item = nothing in some cases.
I loop through all items in a listview (count = 39). For items 0 to 15 everything works great, for item 16 above code returns item = nothing and there for my function returns nothing. So the checkbox I want to uncheck is not found.
I tried to make all listview items visible (like Juan suggested), but that doesn't do the trick.
Does any one have a solution for me?
Best regards,
Frank


