When NOT autogenerating columns, it makes sense why the DataContext for a CellTemplate would be the entire row/object. In this case when AutoGenerateColumns=False, the XAML developer is required to manually set up the data bindings; the column/property names are known to the developer. This feature allows for multiple cell values to be combined into a single grid cell for some interesting features.
However, it makes templating cells very difficult when AutoGeneratingColumns=True because often the reason why the columns are generated automatically is that the specific names of the columns are not known ahead of time; the columns may be dynamic. And, it's extremely difficult to get the value of the grid cell when automatically generating columns. The bindings could be reset for every cell, but I've seen where this is not recommended, and I completely agree.
One technique I've seen is to have the DataContext of an element within the DataTemplate to scan the VisualTree hierarchy for the GridViewCell:
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type gridView:GridViewCell}}"
This works unless you attempt to use this DataContext for a property, like a ToolTip, that doesn't live in the same VisualTree. Attempting to bind to the parent DataContext will yield a "Cannot find element that provides DataContext." binding error during runtime.
Of course, instead of a DataRow, each row could be a collection of MVVM objects instead of a DataTable, and I could put metadata in the objects to be used to style the cells. Unfortunately, this seems extremely inefficient and clunky.
A CellTemplateSelector with a well-crafted attached property seems to be the right way to go. The SelectTemplate item parameter has the GridViewColumn but the value within the DataTemplate is still a DataRow.
I could convert each grid cell value with an IValueConverter into an object that pairs the cell data value with some metadata that could be used for selecting a template, but the specific column is not sent into the Convert method, so I wouldn't know what to convert it to.
I've seen many versions of this problem on your forum, but there are either no answers, or their touched on so vaguely that there not useful. Do you know of a comprehensive and elegant solution to this common problem?
However, it makes templating cells very difficult when AutoGeneratingColumns=True because often the reason why the columns are generated automatically is that the specific names of the columns are not known ahead of time; the columns may be dynamic. And, it's extremely difficult to get the value of the grid cell when automatically generating columns. The bindings could be reset for every cell, but I've seen where this is not recommended, and I completely agree.
One technique I've seen is to have the DataContext of an element within the DataTemplate to scan the VisualTree hierarchy for the GridViewCell:
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type gridView:GridViewCell}}"
This works unless you attempt to use this DataContext for a property, like a ToolTip, that doesn't live in the same VisualTree. Attempting to bind to the parent DataContext will yield a "Cannot find element that provides DataContext." binding error during runtime.
Of course, instead of a DataRow, each row could be a collection of MVVM objects instead of a DataTable, and I could put metadata in the objects to be used to style the cells. Unfortunately, this seems extremely inefficient and clunky.
A CellTemplateSelector with a well-crafted attached property seems to be the right way to go. The SelectTemplate item parameter has the GridViewColumn but the value within the DataTemplate is still a DataRow.
I could convert each grid cell value with an IValueConverter into an object that pairs the cell data value with some metadata that could be used for selecting a template, but the specific column is not sent into the Convert method, so I wouldn't know what to convert it to.
I've seen many versions of this problem on your forum, but there are either no answers, or their touched on so vaguely that there not useful. Do you know of a comprehensive and elegant solution to this common problem?
26 Answers, 1 is accepted
0
Hello,
Working with the visual elements and RelativeSource binding would not be a recommended approach as the Name scope is not reliable. Since the RadGridView supports UI Virtualization, its rows/cells are reused and that is why we cannot rely on the visual elements. You can check our online documentation for a further reference.
The approach we can suggest is working with the bound data items. This is how RadGridView is designed to work.
If you have a specific problem you are working on, may I ask you to open a support thread and attach a solution demonstrating your specific implementation that cannot work with the suggested approach? That way I can check it locally and advise further.
Regards,
Didie
Telerik
Working with the visual elements and RelativeSource binding would not be a recommended approach as the Name scope is not reliable. Since the RadGridView supports UI Virtualization, its rows/cells are reused and that is why we cannot rely on the visual elements. You can check our online documentation for a further reference.
The approach we can suggest is working with the bound data items. This is how RadGridView is designed to work.
If you have a specific problem you are working on, may I ask you to open a support thread and attach a solution demonstrating your specific implementation that cannot work with the suggested approach? That way I can check it locally and advise further.
Regards,
Didie
Telerik
Check out the new Telerik Platform - the only modular platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native apps. Register for the free online keynote and webinar to learn more about the Platform on Wednesday, February 12, 2014 at 11:00 a.m. ET (8:00 a.m. PT).
0

AdaDog
Top achievements
Rank 1
answered on 18 Feb 2014, 01:50 AM
Thanks Didie. I submitted a support ticket with a sample solution.
0
Hello,
Generally I would recommend using a DataTable.DefaultView as an ItemsSource, because DataView is INotifyCollectionChanged and DataRowView is INotifyPropertyChanged and you will get changes automatically.
That way the ToolTip will be shown correctly.
Does the approach using CellTemplateSelector to return a proper template with right values based on the column works fine for you instead of using RelativeSource Binding?
Regards,
Didie
Telerik
Generally I would recommend using a DataTable.DefaultView as an ItemsSource, because DataView is INotifyCollectionChanged and DataRowView is INotifyPropertyChanged and you will get changes automatically.
As to the Binding expression error on the TextBlock in TextBlock.ToolTip, the DataContext of the ToolTip will be the parent GridViewCell.
I have tested it with the following template, defining the ToolTip like so:
<
DataTemplate
>
<
TextBlock
DataContext
=
"{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type telerik:GridViewCell}}}"
Text
=
"Test Column, Point for ToolTip"
>
<
TextBlock.ToolTip
>
<
TextBlock
Text
=
"{Binding Value, StringFormat='The tooltip text is \{0:N3\}'}"
/>
</
TextBlock.ToolTip
>
</
TextBlock
>
</
DataTemplate
>
Does the approach using CellTemplateSelector to return a proper template with right values based on the column works fine for you instead of using RelativeSource Binding?
Regards,
Didie
Telerik
0

AdaDog
Top achievements
Rank 1
answered on 19 Feb 2014, 02:34 PM
Thanks for the code, but the code you provided is no different than what I sent to you.
In answer to your questions "Does the approach using CellTemplateSelector to return a proper template based on the column works fine for you instead of using RelativeSource Binding?"... Even if you use a CellTemplateSelector, which I am (see sample code), the selected template still requires bindings, and the context within the DataTemplate that the CellTemplateSelector chooses is always a DataRow or a DataRowView.
Per your statement "instead of using RelativeSource Binding," are you saying that the DataContext within the DataTemplate can be changed by the CellTemplateSelector?
Is it possible to set the GridViewDataColumn.DataMemberBinding property within the RadGridView.AutoGeneratingColumn event so that the column's DataContext is the value within the cell instead of the entire DataRowView? Otherwise, I don't see how you access the cell's value within a CellTemplateSelector-selected DataTemplate that contains bindings without using RelativeSource. And RelativeSource is not recommended by Telerik or others because some objects within the DataTemplate may not belong in the VisualTree, like tool tips. And, even using RelativeSource within a tooltip results in the following error:
"System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='Telerik.Windows.Controls.GridView.GridViewCell', AncestorLevel='1''. BindingExpression:Path=; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'DataContext' (type 'Object')."
Also, have you looked at the code that I sent? It gives clear insight into the challenge and is using a CellTemplateSelector, an and an attached property for setting the selector on each column along with several different ways of binding within the selected DataTemplate, all of which work and clearly show the binding errors. Changing the ItemsSource from DataTable to DataTable.DefaultView makes no difference in the case of DataTemplate DataContexts.
In answer to your questions "Does the approach using CellTemplateSelector to return a proper template based on the column works fine for you instead of using RelativeSource Binding?"... Even if you use a CellTemplateSelector, which I am (see sample code), the selected template still requires bindings, and the context within the DataTemplate that the CellTemplateSelector chooses is always a DataRow or a DataRowView.
Per your statement "instead of using RelativeSource Binding," are you saying that the DataContext within the DataTemplate can be changed by the CellTemplateSelector?
Is it possible to set the GridViewDataColumn.DataMemberBinding property within the RadGridView.AutoGeneratingColumn event so that the column's DataContext is the value within the cell instead of the entire DataRowView? Otherwise, I don't see how you access the cell's value within a CellTemplateSelector-selected DataTemplate that contains bindings without using RelativeSource. And RelativeSource is not recommended by Telerik or others because some objects within the DataTemplate may not belong in the VisualTree, like tool tips. And, even using RelativeSource within a tooltip results in the following error:
"System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='Telerik.Windows.Controls.GridView.GridViewCell', AncestorLevel='1''. BindingExpression:Path=; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'DataContext' (type 'Object')."
Also, have you looked at the code that I sent? It gives clear insight into the challenge and is using a CellTemplateSelector, an and an attached property for setting the selector on each column along with several different ways of binding within the selected DataTemplate, all of which work and clearly show the binding errors. Changing the ItemsSource from DataTable to DataTable.DefaultView makes no difference in the case of DataTemplate DataContexts.
0

AdaDog
Top achievements
Rank 1
answered on 19 Feb 2014, 02:56 PM
Here's the complete code that I sent.
MainWindow.xaml
​
MainWindow.xaml.cs
App.xaml
DataTemplates.xaml
Converters.xaml
ViewModelBase.cs
MainViewModel.cs
DataViewModel.cs
DataCreator.cs
ColumnMetadata.cs
MainView.xaml
DataView.xaml
DataRowToValueConverter.cs
ColumnDataTemplate.cs
RadGridViewHelper.cs
TemplateSelector.cs
MainWindow.xaml
​
<
Window
x:Class
=
"DynamicColumns.MainWindow"
Title
=
"Dynamic Columns"
Height
=
"350"
Width
=
"525"
>
<
Grid
>
<
ContentPresenter
Content
=
"{Binding}"
/>
</
Grid
>
</
Window
>
MainWindow.xaml.cs
namespace
DynamicColumns
{
using
DynamicColumns.ViewModels;
public
partial
class
MainWindow
{
public
MainWindow()
{
InitializeComponent();
this
.DataContext =
new
MainViewModel();
}
}
}
App.xaml
<
Application
x:Class
=
"DynamicColumns.App"
StartupUri
=
"MainWindow.xaml"
>
<
Application.Resources
>
<
ResourceDictionary
>
<
ResourceDictionary.MergedDictionaries
>
<
ResourceDictionary
Source
=
"DataTemplates.xaml"
/>
<
ResourceDictionary
Source
=
"Converters.xaml"
/>
</
ResourceDictionary.MergedDictionaries
>
</
ResourceDictionary
>
</
Application.Resources
>
</
Application
>
DataTemplates.xaml
<
ResourceDictionary
xmlns
=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:ViewModels
=
"clr-namespace:DynamicColumns.ViewModels"
xmlns:Views
=
"clr-namespace:DynamicColumns.Views"
>
<
DataTemplate
DataType
=
"{x:Type ViewModels:MainViewModel}"
>
<
Views:MainView
/>
</
DataTemplate
>
<
DataTemplate
DataType
=
"{x:Type ViewModels:DataViewModel}"
>
<
Views:DataView
/>
</
DataTemplate
>
</
ResourceDictionary
>
Converters.xaml
<
ResourceDictionary
xmlns
=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:Converters
=
"clr-namespace:DynamicColumns.Converters"
>
<
Converters:DataRowToValueConverter
x:Key
=
"DataRowToValueConverter"
/>
</
ResourceDictionary
>
ViewModelBase.cs
namespace
DynamicColumns.ViewModels
{
using
System;
using
System.Collections.Specialized;
using
System.ComponentModel;
using
System.Linq.Expressions;
public
class
ViewModelBase : INotifyPropertyChanged, INotifyPropertyChanging
{
#region Events
#region INotifyPropertyChanged Members
public
event
PropertyChangedEventHandler PropertyChanged;
public
event
PropertyChangingEventHandler PropertyChanging;
#endregion INotifyPropertyChanged Members
#endregion Events
#region Methods
#endregion Methods
#region Helper Methods
protected
void
RaisePropertyChanged(
string
propertyName)
{
this
.OnPropertyChanged(propertyName);
}
private
void
OnPropertyChanged<T>(Expression<Func<T>> expression)
{
MemberExpression memberExpression = (MemberExpression)expression.Body;
this
.OnPropertyChanged(memberExpression.Member.Name);
}
protected
void
RaisePropertyChanged<T>(Expression<Func<T>> expression)
{
this
.OnPropertyChanged(expression);
}
protected
virtual
void
OnPropertyChanged(
string
propertyName)
{
if
(
this
.PropertyChanged !=
null
)
{
this
.PropertyChanged(
this
,
new
PropertyChangedEventArgs(propertyName));
}
}
protected
void
RaisePropertyChanging(
string
propertyName)
{
this
.OnPropertyChanging(propertyName);
if
(
this
.PropertyChanging !=
null
)
{
this
.PropertyChanging(
this
,
new
PropertyChangingEventArgs(propertyName));
}
}
protected
void
RaisePropertyChanging<T>(Expression<Func<T>> expression)
{
MemberExpression memberExpression = (MemberExpression)expression.Body;
this
.OnPropertyChanging(memberExpression.Member.Name);
}
protected
virtual
void
OnPropertyChanging(
string
propertyName)
{
if
(
this
.PropertyChanging !=
null
)
{
this
.PropertyChanging(
this
,
new
PropertyChangingEventArgs(propertyName));
}
}
protected
void
Register<V>(V viewModel, PropertyChangedEventHandler handler)
where V :
class
, INotifyPropertyChanged
{
if
(viewModel !=
null
)
{
viewModel.PropertyChanged += handler;
}
}
protected
void
Unregister<V>(V viewModel, PropertyChangedEventHandler handler)
where V :
class
, INotifyPropertyChanged
{
if
(viewModel !=
null
)
{
viewModel.PropertyChanged -= handler;
}
}
protected
void
Register<V>(NotifyCollectionChangedEventArgs e, PropertyChangedEventHandler handler)
where V :
class
, INotifyPropertyChanged
{
if
(e.NewItems !=
null
&& e.NewItems.Count > 0)
{
foreach
(
object
item
in
e.NewItems)
{
if
(item
is
V)
{
V viewModel = (V)item;
this
.Register(viewModel, handler);
}
}
}
if
(e.OldItems !=
null
&& e.OldItems.Count > 0)
{
foreach
(
object
item
in
e.OldItems)
{
if
(item
is
V)
{
V viewModel = (V)item;
this
.Unregister(viewModel, handler);
}
}
}
}
#endregion Helper Methods
}
}
MainViewModel.cs
namespace
DynamicColumns.ViewModels
{
public
class
MainViewModel : ViewModelBase
{
private
DataViewModel data;
public
MainViewModel()
{
this
.Data =
new
DataViewModel();
}
public
DataViewModel Data
{
get
{
return
this
.data;
}
set
{
this
.data = value;
this
.RaisePropertyChanged(() =>
this
.Data);
}
}
}
}
DataViewModel.cs
namespace
DynamicColumns.ViewModels
{
using
System.Data;
using
System.Windows.Input;
using
DynamicColumns.Data;
using
Microsoft.Practices.Prism.Commands;
public
class
DataViewModel : ViewModelBase
{
private
ICommand loadCommand;
private
DataTable dataTable;
public
DataTable DataTable
{
get
{
return
this
.dataTable;
}
set
{
this
.dataTable = value;
this
.RaisePropertyChanged(() =>
this
.DataTable);
}
}
public
ICommand LoadCommand
{
get
{
return
this
.loadCommand ?? (
this
.loadCommand =
new
DelegateCommand(
this
.Load));
}
}
private
void
Load()
{
this
.DataTable = DataCreator.Create();
}
}
}
DataCreator.cs
namespace
DynamicColumns.Data
{
using
System;
using
System.Collections.Generic;
using
System.Data;
public
static
class
DataCreator
{
static
Random columnNumberRandom =
new
Random();
static
Random columnPrecisionRandom =
new
Random();
static
Random rowCountRandom =
new
Random();
static
Random doubleRandom =
new
Random();
public
static
List<ColumnMetadata> ColumnMetadatas
{
get
;
private
set
;
}
public
static
DataTable Create()
{
DataTable dataTable =
new
DataTable();
AddColumns(dataTable);
AddData(dataTable);
return
dataTable;
}
private
static
void
AddColumns(DataTable dataTable)
{
int
columnCount = columnNumberRandom.Next(10, 30);
ColumnMetadatas =
new
List<ColumnMetadata>();
for
(
int
i = 0; i < columnCount; i++)
{
string
columnName =
string
.Format(
"Column{0}"
, i);
int
columnPrecision = columnPrecisionRandom.Next(5);
string
columnAlias =
string
.Format(
"Column #{0}, Precision {1}"
, i, columnPrecision);
ColumnMetadatas.Add(
new
ColumnMetadata() { Alias = columnAlias, Name = columnName, Precision = columnPrecision});
DataColumn dataColumn =
new
DataColumn(columnName,
typeof
(
double
));
dataTable.Columns.Add(dataColumn);
}
}
private
static
void
AddData(DataTable dataTable)
{
int
rowCount = rowCountRandom.Next(200);
for
(
int
i = 0; i < rowCount; i++)
{
List<
object
> list =
new
List<
object
>();
for
(
int
j = 0; j < dataTable.Columns.Count; j++)
{
double
value = doubleRandom.NextDouble() * 100;
list.Add(value);
}
dataTable.Rows.Add(list.ToArray());
}
}
}
}
ColumnMetadata.cs
namespace
DynamicColumns.Data
{
public
class
ColumnMetadata
{
public
string
Name
{
get
;
set
;
}
public
string
Alias
{
get
;
set
;
}
public
int
Precision
{
get
;
set
;
}
}
}
MainView.xaml
<
UserControl
x:Class
=
"DynamicColumns.Views.MainView"
mc:Ignorable
=
"d"
d:DesignHeight
=
"300"
d:DesignWidth
=
"300"
>
<
Grid
Background
=
"Red"
>
<
Grid
>
<
Grid.RowDefinitions
>
<
RowDefinition
Height
=
"Auto"
/>
<
RowDefinition
Height
=
"*"
/>
</
Grid.RowDefinitions
>
<
ContentPresenter
Grid.Row
=
"1"
Content
=
"{Binding Data}"
/>
</
Grid
>
</
Grid
>
</
UserControl
>
DataView.xaml
<
UserControl
x:Class
=
"DynamicColumns.Views.DataView"
xmlns:telerik
=
"http://schemas.telerik.com/2008/xaml/presentation"
xmlns:Attached
=
"clr-namespace:DynamicColumns.Attached"
mc:Ignorable
=
"d"
d:DesignHeight
=
"300"
d:DesignWidth
=
"300"
>
<
Grid
>
<
Grid
>
<
Grid.RowDefinitions
>
<
RowDefinition
Height
=
"*"
/>
<
RowDefinition
Height
=
"Auto"
/>
</
Grid.RowDefinitions
>
<
telerik:RadGridView
Grid.Row
=
"0"
ItemsSource
=
"{Binding DataTable.DefaultView}"
>
<
Attached:RadGridViewDataHelper.Helper
>
<
Attached:RadGridViewDataHelper
>
<
Attached:RadGridViewDataHelper.TemplateSelector
>
<
Attached:TemplateSelector
>
<
Attached:TemplateSelector.ColumnDataTemplates
>
<
Attached:ColumnDataTemplate
ColumnPrecision
=
"0"
>
<
Attached:ColumnDataTemplate.DataTemplate
>
<
DataTemplate
>
<
TextBlock
DataContext
=
"{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type telerik:GridViewCell}}}"
Text
=
"{Binding Value, StringFormat={}{0:N0}}"
/>
</
DataTemplate
>
</
Attached:ColumnDataTemplate.DataTemplate
>
</
Attached:ColumnDataTemplate
>
<
Attached:ColumnDataTemplate
ColumnPrecision
=
"1"
>
<
Attached:ColumnDataTemplate.DataTemplate
>
<
DataTemplate
>
<
TextBlock
DataContext
=
"{Binding Path=Value, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type telerik:GridViewCell}}}"
Text
=
"{Binding StringFormat={}{0:N1}}"
/>
</
DataTemplate
>
</
Attached:ColumnDataTemplate.DataTemplate
>
</
Attached:ColumnDataTemplate
>
<
Attached:ColumnDataTemplate
ColumnPrecision
=
"2"
>
<
Attached:ColumnDataTemplate.DataTemplate
>
<
DataTemplate
>
<
TextBlock
Text
=
"{Binding Converter={StaticResource DataRowToValueConverter}, StringFormat={}{0:N2}}"
/>
</
DataTemplate
>
</
Attached:ColumnDataTemplate.DataTemplate
>
</
Attached:ColumnDataTemplate
>
<
Attached:ColumnDataTemplate
ColumnPrecision
=
"3"
>
<
Attached:ColumnDataTemplate.DataTemplate
>
<
DataTemplate
>
<!--Causes binding warning => System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='Telerik.Windows.Controls.GridView.GridViewCell', AncestorLevel='1''. BindingExpression:Path=Value; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'DataContext' (type 'Object')-->
<
TextBlock
DataContext
=
"{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type telerik:GridViewCell}}}"
Text
=
"{Binding Value, StringFormat={}{0:N3}}"
>
<
TextBlock.ToolTip
>
<
TextBlock
Text
=
"{Binding Value, StringFormat='The tooltip text is \{0:N3\}'}"
/>
</
TextBlock.ToolTip
>
</
TextBlock
>
</
DataTemplate
>
</
Attached:ColumnDataTemplate.DataTemplate
>
</
Attached:ColumnDataTemplate
>
<
Attached:ColumnDataTemplate
ColumnPrecision
=
"4"
>
<
Attached:ColumnDataTemplate.DataTemplate
>
<
DataTemplate
>
<!--Causes binding warning => System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='Telerik.Windows.Controls.GridView.GridViewCell', AncestorLevel='1''. BindingExpression:Path=Value; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'DataContext' (type 'Object')-->
<
TextBlock
DataContext
=
"{Binding Path=Value, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type telerik:GridViewCell}}}"
Text
=
"{Binding StringFormat={}{0:N4}}"
>
<
TextBlock.ToolTip
>
<
TextBlock
Text
=
"{Binding StringFormat='The tooltip text is \{0:N4\}'}"
/>
</
TextBlock.ToolTip
>
</
TextBlock
>
</
DataTemplate
>
</
Attached:ColumnDataTemplate.DataTemplate
>
</
Attached:ColumnDataTemplate
>
<
Attached:ColumnDataTemplate
ColumnPrecision
=
"5"
>
<
Attached:ColumnDataTemplate.DataTemplate
>
<
DataTemplate
>
<
TextBlock
Text
=
"{Binding StringFormat={}{0:N5}}"
/>
</
DataTemplate
>
</
Attached:ColumnDataTemplate.DataTemplate
>
</
Attached:ColumnDataTemplate
>
</
Attached:TemplateSelector.ColumnDataTemplates
>
</
Attached:TemplateSelector
>
</
Attached:RadGridViewDataHelper.TemplateSelector
>
</
Attached:RadGridViewDataHelper
>
</
Attached:RadGridViewDataHelper.Helper
>
</
telerik:RadGridView
>
<
Button
Grid.Row
=
"1"
Content
=
"Load"
Command
=
"{Binding LoadCommand}"
/>
</
Grid
>
</
Grid
>
</
UserControl
>
DataRowToValueConverter.cs
namespace
DynamicColumns.Converters
{
using
System;
using
System.Globalization;
using
System.Windows.Data;
public
class
DataRowToValueConverter : IValueConverter
{
public
object
Convert(
object
value, Type targetType,
object
parameter, CultureInfo culture)
{
return
value;
}
public
object
ConvertBack(
object
value, Type targetType,
object
parameter, CultureInfo culture)
{
throw
new
NotImplementedException();
}
}
}
ColumnDataTemplate.cs
namespace
DynamicColumns.Attached
{
using
System.Windows;
public
class
ColumnDataTemplate
{
public
DataTemplate DataTemplate
{
get
;
set
;
}
public
int
ColumnPrecision
{
get
;
set
;
}
}
}
RadGridViewHelper.cs
namespace
DynamicColumns.Attached
{
using
System.Linq;
using
System.Windows;
using
DynamicColumns.Data;
using
Telerik.Windows.Controls;
public
class
RadGridViewDataHelper : DependencyObject
{
#region Fields
private
RadGridView radGridView;
#endregion Fields
#region Dependency Properties
public
static
readonly
DependencyProperty TemplateSelectorProperty = DependencyProperty.Register(
"TemplateSelector"
,
typeof
(TemplateSelector),
typeof
(RadGridViewDataHelper),
new
PropertyMetadata(TemplateSelectorChanged));
private
static
void
TemplateSelectorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public
static
readonly
DependencyProperty HelperProperty = DependencyProperty.RegisterAttached(
"Helper"
,
typeof
(RadGridViewDataHelper),
typeof
(RadGridView),
new
PropertyMetadata(HelperChanged));
#endregion Dependency Properties
#region Dependency Property Methods
public
static
void
SetHelper(RadGridView radGridView, RadGridViewDataHelper value)
{
radGridView.SetValue(HelperProperty, value);
}
public
static
RadGridViewDataHelper GetHelper(RadGridView itemsControl)
{
return
(RadGridViewDataHelper)itemsControl.GetValue(HelperProperty);
}
public
TemplateSelector TemplateSelector
{
get
{
return
this
.GetValue(TemplateSelectorProperty)
as
TemplateSelector;
}
set
{
this
.SetValue(TemplateSelectorProperty, value);
}
}
#endregion Dependency Property Methods
#region Dependency Property Handler
private
static
void
HelperChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if
(d
is
RadGridView && e.NewValue
is
RadGridViewDataHelper)
{
RadGridViewDataHelper radGridViewHelper = ((RadGridViewDataHelper)e.NewValue);
radGridViewHelper.Initialize((RadGridView)d);
}
}
#endregion Dependency Property Handler
private
void
RadGridView_AutoGeneratingColumn(
object
sender, GridViewAutoGeneratingColumnEventArgs e)
{
RadGridView gridView = sender
as
RadGridView;
if
(gridView !=
null
)
{
this
.InitializeColumnHeader(e.Column);
this
.InitializeCellTemplate(e.Column);
}
}
private
void
InitializeColumnHeader(GridViewColumn gridViewColumn)
{
ColumnMetadata columnMetadata = DataCreator.ColumnMetadatas.FirstOrDefault(c => c.Name == gridViewColumn.UniqueName);
if
(columnMetadata !=
null
)
{
gridViewColumn.Header = columnMetadata.Alias;
}
}
private
void
InitializeCellTemplate(GridViewColumn gridViewColumn)
{
gridViewColumn.CellTemplateSelector =
this
.TemplateSelector;
//GridViewDataColumn gridViewDataColumn = (GridViewDataColumn)gridViewColumn;
//Binding binding = new Binding(gridViewDataColumn.UniqueName);
//binding.Mode = BindingMode.TwoWay;
//gridViewDataColumn.DataMemberBinding = binding;
}
private
void
Initialize(RadGridView radGridView)
{
this
.radGridView = radGridView;
this
.radGridView.AutoGeneratingColumn += RadGridView_AutoGeneratingColumn;
}
}
}
TemplateSelector.cs
namespace
DynamicColumns.Attached
{
using
System.Collections.Generic;
using
System.Linq;
using
System.Windows;
using
System.Windows.Controls;
using
DynamicColumns.Data;
using
Telerik.Windows.Controls.GridView;
public
class
TemplateSelector : DataTemplateSelector
{
private
List<ColumnDataTemplate> columnDataTemplates =
new
List<ColumnDataTemplate>();
public
List<ColumnDataTemplate> ColumnDataTemplates
{
get
{
return
this
.columnDataTemplates;
}
}
public
override
DataTemplate SelectTemplate(
object
item, DependencyObject container)
{
DataTemplate dataTemplate =
base
.SelectTemplate(item, container);
GridViewCell gridViewCell = container
as
GridViewCell;
if
(gridViewCell !=
null
&& gridViewCell.Column !=
null
)
{
ColumnMetadata columnMetadata = DataCreator.ColumnMetadatas.FirstOrDefault(c => c.Name == gridViewCell.Column.UniqueName);
if
(columnMetadata !=
null
)
{
ColumnDataTemplate columnDataTemplate =
this
.ColumnDataTemplates.FirstOrDefault(c => c.ColumnPrecision == columnMetadata.Precision);
if
(columnDataTemplate !=
null
)
{
dataTemplate = columnDataTemplate.DataTemplate;
}
}
}
return
dataTemplate;
}
}
}
0
Hello,
How does this approach works for you?
Regards,
Didie
Telerik
The alternative option I can suggest would be setting the DataContext of the TextBlock (defined in XAML) in code. That way you will avoid the RelativeSource Binding.
For example:
<
DataTemplate
>
<
TextBlock
Loaded
=
"TextBlock_Loaded_1"
Text
=
"DataContext set in code"
>
<
TextBlock.ToolTip
>
<
TextBlock
Text
=
"{Binding Value, StringFormat='The tooltip text is \{0:N3\}'}"
/>
</
TextBlock.ToolTip
>
</
TextBlock
>
</
DataTemplate
>
private
void
TextBlock_Loaded_1(
object
sender, RoutedEventArgs e)
{
var textBlock = (sender
as
TextBlock);
textBlock.DataContext = textBlock.Parent
as
GridViewCell;
}
How does this approach works for you?
Regards,
Didie
Telerik
0

AdaDog
Top achievements
Rank 1
answered on 19 Feb 2014, 04:16 PM
It's okay. I'd rather not assume the content of a DataTemplate will be a TextBlock, and I'm not a fan of code-behind. Also, setting the DataContext for every TextBlock seems very expensive.
I can solve the TextBlock issue by using the FrameworkElement lowest common denominator as in the following.
​
I would prefer do do the work in the TemplateSelector as in
but at this point the Parent property is null.
It's not the most elegant solution, but, if it's not too expensive, then I guess I can live with it.
Thanks, Didie.
I can solve the TextBlock issue by using the FrameworkElement lowest common denominator as in the following.
​
private
void
FrameworkElement_OnLoaded(
object
sender, RoutedEventArgs e)
{
FrameworkElement frameworkElement = (FrameworkElement)sender;
GridViewCell gridViewCell = frameworkElement.Parent
as
GridViewCell;
frameworkElement.DataContext = gridViewCell;
}
I would prefer do do the work in the TemplateSelector as in
public
override
DataTemplate SelectTemplate(
object
item, DependencyObject container)
{
DataTemplate dataTemplate =
base
.SelectTemplate(item, container);
GridViewCell gridViewCell = container
as
GridViewCell;
if
(gridViewCell !=
null
&& gridViewCell.Column !=
null
)
{
ColumnMetadata columnMetadata = DataCreator.ColumnMetadatas.FirstOrDefault(c => c.Name == gridViewCell.Column.UniqueName);
if
(columnMetadata !=
null
)
{
ColumnDataTemplate columnDataTemplate =
this
.ColumnDataTemplates.FirstOrDefault(c => c.ColumnPrecision == columnMetadata.Precision);
if
(columnDataTemplate !=
null
)
{
dataTemplate = columnDataTemplate.DataTemplate;
DependencyObject dependencyObject = dataTemplate.LoadContent();
FrameworkElement frameworkElement = (FrameworkElement)dependencyObject;
if
(frameworkElement !=
null
&& frameworkElement.Parent
is
GridViewCell)
{
frameworkElement.DataContext = frameworkElement.Parent;
}
else
{
frameworkElement.Loaded += frameworkElement_Loaded;
}
}
}
}
return
dataTemplate;
}
private
void
frameworkElement_Loaded(
object
sender, RoutedEventArgs e)
{
FrameworkElement frameworkElement = (FrameworkElement)sender;
GridViewCell gridViewCell = frameworkElement.Parent
as
GridViewCell;
frameworkElement.DataContext = gridViewCell;
}
but at this point the Parent property is null.
It's not the most elegant solution, but, if it's not too expensive, then I guess I can live with it.
Thanks, Didie.
0

AdaDog
Top achievements
Rank 1
answered on 19 Feb 2014, 04:32 PM
Even with the code-behind setting the DataContext of the FrameworkElement within the DataTemplate
still causes the following binding error:
System.Windows.Data Error: 40 : BindingExpression path error: 'Value' property not found on 'object' ''DataRowView' (HashCode=51955039)'. BindingExpression:Path=Value; DataItem='DataRowView' (HashCode=51955039); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
... but the tooltip shows correctly. How do I get rid of the binding error?
<
DataTemplate
>
<
TextBlock
Loaded
=
"FrameworkElement_OnLoaded"
Text
=
"{Binding Value, StringFormat={}{0:N2}}"
>
<
TextBlock.ToolTip
>
<
TextBlock
Text
=
"{Binding Value, StringFormat='The tooltip text is \{0:N2\}'}"
/>
</
TextBlock.ToolTip
>
</
TextBlock
>
</
DataTemplate
>
still causes the following binding error:
System.Windows.Data Error: 40 : BindingExpression path error: 'Value' property not found on 'object' ''DataRowView' (HashCode=51955039)'. BindingExpression:Path=Value; DataItem='DataRowView' (HashCode=51955039); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
... but the tooltip shows correctly. How do I get rid of the binding error?
0

AdaDog
Top achievements
Rank 1
answered on 19 Feb 2014, 04:36 PM
Even this (without the tooltip) causes binding errors.
<
DataTemplate
>
<
TextBlock
Loaded
=
"FrameworkElement_OnLoaded"
Text
=
"{Binding Value, StringFormat={}{0:N2}}"
>
</
TextBlock
>
</
DataTemplate
>
0
0

AdaDog
Top achievements
Rank 1
answered on 19 Feb 2014, 04:44 PM
That's what I thought. Here's the entire new DataView.xaml.
The binding error still occurs. Can you see it in your Debug window as well when you run it?
<
UserControl
x:Class
=
"DynamicColumns.Views.DataView"
xmlns:telerik
=
"http://schemas.telerik.com/2008/xaml/presentation"
xmlns:Attached
=
"clr-namespace:DynamicColumns.Attached"
mc:Ignorable
=
"d"
d:DesignHeight
=
"300"
d:DesignWidth
=
"300"
>
<
Grid
>
<
Grid
>
<
Grid.RowDefinitions
>
<
RowDefinition
Height
=
"*"
/>
<
RowDefinition
Height
=
"Auto"
/>
</
Grid.RowDefinitions
>
<
telerik:RadGridView
Grid.Row
=
"0"
ItemsSource
=
"{Binding DataTable.DefaultView}"
>
<
Attached:RadGridViewDataHelper.Helper
>
<
Attached:RadGridViewDataHelper
>
<
Attached:RadGridViewDataHelper.TemplateSelector
>
<
Attached:TemplateSelector
>
<
Attached:TemplateSelector.ColumnDataTemplates
>
<
Attached:ColumnDataTemplate
ColumnPrecision
=
"2"
>
<
Attached:ColumnDataTemplate.DataTemplate
>
<
DataTemplate
>
<
TextBlock
Loaded
=
"FrameworkElement_OnLoaded"
Text
=
"{Binding Value, StringFormat={}{0:N2}}"
>
</
TextBlock
>
</
DataTemplate
>
</
Attached:ColumnDataTemplate.DataTemplate
>
</
Attached:ColumnDataTemplate
>
</
Attached:TemplateSelector.ColumnDataTemplates
>
</
Attached:TemplateSelector
>
</
Attached:RadGridViewDataHelper.TemplateSelector
>
</
Attached:RadGridViewDataHelper
>
</
Attached:RadGridViewDataHelper.Helper
>
</
telerik:RadGridView
>
<
Button
Grid.Row
=
"1"
Content
=
"Load"
Command
=
"{Binding LoadCommand}"
/>
</
Grid
>
</
Grid
>
</
UserControl
>
The binding error still occurs. Can you see it in your Debug window as well when you run it?
0

AdaDog
Top achievements
Rank 1
answered on 19 Feb 2014, 05:08 PM
I added a converter to see what was going on
DataView.xaml
DataRowToValueConverter.cs
I get the following debug output:
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Value is null
Value is null
Value is null
... in the attached image of the application.
"Not a GridViewCell. It's a DataRowView" seems to occur for each visible cell, but I don't understand why "Value is null" would appear 3 times.
DataView.xaml
<
UserControl
x:Class
=
"DynamicColumns.Views.DataView"
xmlns:telerik
=
"http://schemas.telerik.com/2008/xaml/presentation"
xmlns:Attached
=
"clr-namespace:DynamicColumns.Attached"
mc:Ignorable
=
"d"
d:DesignHeight
=
"300"
d:DesignWidth
=
"300"
>
<
Grid
>
<
Grid
>
<
Grid.RowDefinitions
>
<
RowDefinition
Height
=
"*"
/>
<
RowDefinition
Height
=
"Auto"
/>
</
Grid.RowDefinitions
>
<
telerik:RadGridView
Grid.Row
=
"0"
ItemsSource
=
"{Binding DataTable.DefaultView}"
>
<
Attached:RadGridViewDataHelper.Helper
>
<
Attached:RadGridViewDataHelper
>
<
Attached:RadGridViewDataHelper.TemplateSelector
>
<
Attached:TemplateSelector
>
<
Attached:TemplateSelector.ColumnDataTemplates
>
<
Attached:ColumnDataTemplate
ColumnPrecision
=
"2"
>
<
Attached:ColumnDataTemplate.DataTemplate
>
<
DataTemplate
>
<
TextBlock
Loaded
=
"FrameworkElement_OnLoaded"
Text
=
"{Binding Converter={StaticResource DataRowToValueConverter}, StringFormat={}{0:N2}}"
>
</
TextBlock
>
</
DataTemplate
>
</
Attached:ColumnDataTemplate.DataTemplate
>
</
Attached:ColumnDataTemplate
>
</
Attached:TemplateSelector.ColumnDataTemplates
>
</
Attached:TemplateSelector
>
</
Attached:RadGridViewDataHelper.TemplateSelector
>
</
Attached:RadGridViewDataHelper
>
</
Attached:RadGridViewDataHelper.Helper
>
</
telerik:RadGridView
>
<
Button
Grid.Row
=
"1"
Content
=
"Load"
Command
=
"{Binding LoadCommand}"
/>
</
Grid
>
</
Grid
>
</
UserControl
>
DataRowToValueConverter.cs
namespace
DynamicColumns.Converters
{
using
System;
using
System.Diagnostics;
using
System.Globalization;
using
System.Windows.Data;
using
Telerik.Windows.Controls.GridView;
public
class
DataRowToValueConverter : IValueConverter
{
public
object
Convert(
object
value, Type targetType,
object
parameter, CultureInfo culture)
{
if
(value
is
GridViewCell)
{
GridViewCell gridViewCell = (GridViewCell)value;
return
gridViewCell.Value;
}
else
if
(value ==
null
)
{
Debug.WriteLine(
"Value is null"
);
}
else
{
Debug.WriteLine(
string
.Format(
"Not a GridViewCell. It's a {0}"
, value.GetType().Name));
}
return
value;
}
public
object
ConvertBack(
object
value, Type targetType,
object
parameter, CultureInfo culture)
{
throw
new
NotImplementedException();
}
}
}
I get the following debug output:
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Value is null
Value is null
Value is null
... in the attached image of the application.
"Not a GridViewCell. It's a DataRowView" seems to occur for each visible cell, but I don't understand why "Value is null" would appear 3 times.
0

AdaDog
Top achievements
Rank 1
answered on 19 Feb 2014, 05:11 PM
I added a converter to see what was going on
View
Converter
I got the following debug output
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Value is null
Value is null
Value is null
...from the attached picture of the application.
"Not a GridViewCell. It's a DataRowView" seems to occur for each cell. I'm not sure what is going on with "Value is null."
View
<
UserControl
x:Class
=
"DynamicColumns.Views.DataView"
xmlns:telerik
=
"http://schemas.telerik.com/2008/xaml/presentation"
xmlns:Attached
=
"clr-namespace:DynamicColumns.Attached"
mc:Ignorable
=
"d"
d:DesignHeight
=
"300"
d:DesignWidth
=
"300"
>
<
Grid
>
<
Grid
>
<
Grid.RowDefinitions
>
<
RowDefinition
Height
=
"*"
/>
<
RowDefinition
Height
=
"Auto"
/>
</
Grid.RowDefinitions
>
<
telerik:RadGridView
Grid.Row
=
"0"
ItemsSource
=
"{Binding DataTable.DefaultView}"
>
<
Attached:RadGridViewDataHelper.Helper
>
<
Attached:RadGridViewDataHelper
>
<
Attached:RadGridViewDataHelper.TemplateSelector
>
<
Attached:TemplateSelector
>
<
Attached:TemplateSelector.ColumnDataTemplates
>
<
Attached:ColumnDataTemplate
ColumnPrecision
=
"2"
>
<
Attached:ColumnDataTemplate.DataTemplate
>
<
DataTemplate
>
<
TextBlock
Loaded
=
"FrameworkElement_OnLoaded"
Text
=
"{Binding Converter={StaticResource DataRowToValueConverter}, StringFormat={}{0:N2}}"
>
</
TextBlock
>
</
DataTemplate
>
</
Attached:ColumnDataTemplate.DataTemplate
>
</
Attached:ColumnDataTemplate
>
</
Attached:TemplateSelector.ColumnDataTemplates
>
</
Attached:TemplateSelector
>
</
Attached:RadGridViewDataHelper.TemplateSelector
>
</
Attached:RadGridViewDataHelper
>
</
Attached:RadGridViewDataHelper.Helper
>
</
telerik:RadGridView
>
<
Button
Grid.Row
=
"1"
Content
=
"Load"
Command
=
"{Binding LoadCommand}"
/>
</
Grid
>
</
Grid
>
</
UserControl
>
Converter
namespace
DynamicColumns.Converters
{
using
System;
using
System.Diagnostics;
using
System.Globalization;
using
System.Windows.Data;
using
Telerik.Windows.Controls.GridView;
public
class
DataRowToValueConverter : IValueConverter
{
public
object
Convert(
object
value, Type targetType,
object
parameter, CultureInfo culture)
{
if
(value
is
GridViewCell)
{
GridViewCell gridViewCell = (GridViewCell)value;
return
gridViewCell.Value;
}
else
if
(value ==
null
)
{
Debug.WriteLine(
"Value is null"
);
}
else
{
Debug.WriteLine(
string
.Format(
"Not a GridViewCell. It's a {0}"
, value.GetType().Name));
}
return
value;
}
public
object
ConvertBack(
object
value, Type targetType,
object
parameter, CultureInfo culture)
{
throw
new
NotImplementedException();
}
}
}
I got the following debug output
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Not a GridViewCell. It's a DataRowView
Value is null
Value is null
Value is null
...from the attached picture of the application.
"Not a GridViewCell. It's a DataRowView" seems to occur for each cell. I'm not sure what is going on with "Value is null."
0
Hello,
I am not able to get the exception in the Output Window. Please refer to the attached solution after my modifications.
Regards,
Didie
Telerik
I am not able to get the exception in the Output Window. Please refer to the attached solution after my modifications.
Regards,
Didie
Telerik
0

AdaDog
Top achievements
Rank 1
answered on 19 Feb 2014, 05:26 PM
You'll get it if you set the TextBlock.Text property to "{Binding Value}" instead of "DataContext set in code."
0
Hi,
It is because the Binding tries to evaluate before the new DataContext has been set on Loaded of the TextBlock. You will have to also assign the Text property in code.
For example:
Please make sure you remove setting the Text in XAML.
Regards,
Didie
Telerik
It is because the Binding tries to evaluate before the new DataContext has been set on Loaded of the TextBlock. You will have to also assign the Text property in code.
For example:
private void TextBlock_Loaded_1(object sender, RoutedEventArgs e)
{
var textBlock = (sender as TextBlock);
var cell = textBlock.Parent as GridViewCell;
textBlock.DataContext = textBlock.Parent as GridViewCell;
textBlock.Text = cell.Value.ToString();
}
Please make sure you remove setting the Text in XAML.
Regards,
Didie
Telerik
0

AdaDog
Top achievements
Rank 1
answered on 19 Feb 2014, 10:28 PM
This is not a very elegant solution. And, what's the point of using a DataTemplate.
The real production application has DataTemplate requirements much more complex than simply setting a Text property in a TextBlock, so this is an unworkable solution.
The bindings work, albeit with errors. Do you know what is passing DataRowView objects into the converter? Do you know what is passing null values into the Converter? Is there a way I can intercept these seemingly redundant calls to the converter?
The real production application has DataTemplate requirements much more complex than simply setting a Text property in a TextBlock, so this is an unworkable solution.
The bindings work, albeit with errors. Do you know what is passing DataRowView objects into the converter? Do you know what is passing null values into the Converter? Is there a way I can intercept these seemingly redundant calls to the converter?
0
Hello,
Indeed, I understand that this solution is not very good. I have consulted the case with the development team and I am afraid we cannot suggest you a better solution based on your requirements. The best option would be to work with the bound data items and their properties. This is by design and this is what we recommend.
Regards,
Didie
Telerik
Indeed, I understand that this solution is not very good. I have consulted the case with the development team and I am afraid we cannot suggest you a better solution based on your requirements. The best option would be to work with the bound data items and their properties. This is by design and this is what we recommend.
Regards,
Didie
Telerik
0
Hello,
I would like to remind you about the other option I have suggested you in the support thread you opened on the same issue. You could build the DataTemplate you return through the CellTemplateSelector in code based on the bound item and the column the parent cell is under, instead of just returning the one you have defined in XAML (having the RelativeSource Binding).
This is where this is currently done in your code:
Regards,
Didie
Telerik
I would like to remind you about the other option I have suggested you in the support thread you opened on the same issue. You could build the DataTemplate you return through the CellTemplateSelector in code based on the bound item and the column the parent cell is under, instead of just returning the one you have defined in XAML (having the RelativeSource Binding).
This is where this is currently done in your code:
GridViewCell gridViewCell = container
as
GridViewCell;
if
(gridViewCell !=
null
&& gridViewCell.Column !=
null
)
{
ColumnMetadata columnMetadata = DataCreator.ColumnMetadatas.FirstOrDefault(c => c.Name == gridViewCell.Column.UniqueName);
if
(columnMetadata !=
null
)
{
ColumnDataTemplate columnDataTemplate =
this
.ColumnDataTemplates.FirstOrDefault(c => c.ColumnPrecision == columnMetadata.Precision);
if
(columnDataTemplate !=
null
)
{
// Build the template now relying on the item
dataTemplate = columnDataTemplate.DataTemplate;
}
}
}
Copy Code
Regards,
Didie
Telerik
0

AdaDog
Top achievements
Rank 1
answered on 20 Feb 2014, 02:11 PM
You posted code that I already posted from my sample solution. See above for all of the code for the solution.
The DataTemplate is already built. It doesn't need to be done in code. It can be done in XAML. The DataContext within the XAML is the same. In this case, with an attached property. The CellTemplateSelector must be set on each column, and since the columns are autogenerated, the selector for each column is set during the autogenerating event. In my case, I chose to use an attached property rather than code-behind, but the effects are the same.
I was already using a CellTemplateSelector in my sample solution. You can set the DataContext for the loaded FrameworkElement in the Loaded event in code-behind to remove the need for a RelativeSource binding. The cell template selection is not the issue. The fact that the binding works is not the issue. The issue is the multiple bindings performed by the RadGridView with different DataContexts having unknown sources, the null values, and the binding errors.
Do you know what is passing DataRowView objects into the converter? What is sending the DataRowView DataContext into the DataTemplate? Why is the DataTemplate used once for each GridCell in the column AND each row in view. Do you know what is passing null values into the Converter? Is there a way I can intercept these seemingly redundant calls on the DataTemplate?
The DataTemplate is already built. It doesn't need to be done in code. It can be done in XAML. The DataContext within the XAML is the same. In this case, with an attached property. The CellTemplateSelector must be set on each column, and since the columns are autogenerated, the selector for each column is set during the autogenerating event. In my case, I chose to use an attached property rather than code-behind, but the effects are the same.
I was already using a CellTemplateSelector in my sample solution. You can set the DataContext for the loaded FrameworkElement in the Loaded event in code-behind to remove the need for a RelativeSource binding. The cell template selection is not the issue. The fact that the binding works is not the issue. The issue is the multiple bindings performed by the RadGridView with different DataContexts having unknown sources, the null values, and the binding errors.
Do you know what is passing DataRowView objects into the converter? What is sending the DataRowView DataContext into the DataTemplate? Why is the DataTemplate used once for each GridCell in the column AND each row in view. Do you know what is passing null values into the Converter? Is there a way I can intercept these seemingly redundant calls on the DataTemplate?
0

AdaDog
Top achievements
Rank 1
answered on 20 Feb 2014, 02:19 PM
I should also say that I AM working with bound data items. The solution has been using bound data items from the beginning: DataTable and DataTable.DefaultView. Are you suggesting to use POC's instead of a DataView? If so, how would you handle an unknown collection shapes? Reflection?
If I knew what was sending a DataRowView and null values into the DataTemplates, then I could probably figure out a workaround.
If I knew what was sending a DataRowView and null values into the DataTemplates, then I could probably figure out a workaround.
0
Hi,
This is exactly my point. You should work with the bound business objects - DataRowView - and its properties and not assigning the DataContext of the TextBlock to be the parent GridViewCell through RelativeSource Binding.
I have posted the code in your TemplateSelector as it is, so that you know where you need to change the current implementation. Instead of returning the DataTemplate defined in XAML, relying on RelativeSource Binding, you should build it in code, setting the Text property of the TextBlock inside to a Property of the DataRowView coming as input parameter to the TemplateSelector.
I am afraid we cannot suggest another solution than those additional code in code behind.
Regards,
Didie
Telerik
This is exactly my point. You should work with the bound business objects - DataRowView - and its properties and not assigning the DataContext of the TextBlock to be the parent GridViewCell through RelativeSource Binding.
I have posted the code in your TemplateSelector as it is, so that you know where you need to change the current implementation. Instead of returning the DataTemplate defined in XAML, relying on RelativeSource Binding, you should build it in code, setting the Text property of the TextBlock inside to a Property of the DataRowView coming as input parameter to the TemplateSelector.
I am afraid we cannot suggest another solution than those additional code in code behind.
Regards,
Didie
Telerik
0

AdaDog
Top achievements
Rank 1
answered on 21 Feb 2014, 10:35 AM
Again, I am not using a RelativeSource binding. I am setting the DataContext of the FrameworkElement/TextBlock in code-behind. This doesn't change the fact that something of type DataRowView, GridViewCell, and null are attempting to use the DataTemplate.
The DataTemplate is already built in XAML. Why build it in code? Even if it is built in code, it will still have the same DataContext as the one in XAML, and the DataContext of the DataTemplate is the issue. A TemplateSelector selects a type of DataTemplate, not of TextBlock.
I'm confused from your last post. Are you saying that DataRowView is a bound business object?
Maybe I'll set a target type on the DataTemplate to see if the Telerik code still attempts to use it. If it breaks, then maybe I can figure out where Telerik is attempting to apply the template for GridViewCell, DataRowView, and null.
I'm sorry you couldn't improve on my code, but I understand. Setting the DataContext of an element inside of a DataTemplate misses the point of DataTemplates. Creating UI objects in code-behind misses the point of a whole bunch of concepts.
The DataTemplate is already built in XAML. Why build it in code? Even if it is built in code, it will still have the same DataContext as the one in XAML, and the DataContext of the DataTemplate is the issue. A TemplateSelector selects a type of DataTemplate, not of TextBlock.
I'm confused from your last post. Are you saying that DataRowView is a bound business object?
Maybe I'll set a target type on the DataTemplate to see if the Telerik code still attempts to use it. If it breaks, then maybe I can figure out where Telerik is attempting to apply the template for GridViewCell, DataRowView, and null.
I'm sorry you couldn't improve on my code, but I understand. Setting the DataContext of an element inside of a DataTemplate misses the point of DataTemplates. Creating UI objects in code-behind misses the point of a whole bunch of concepts.
0
0

AdaDog
Top achievements
Rank 1
answered on 21 Feb 2014, 12:48 PM
It makes sense why each row and cell is attempting to use the DataTemplate. It's too bad that the property name for a column is CellTemplateSelector and not RowAndCellAndUnknownSourceOfNullValueTemplateSelector.
0